Compare commits
455 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ef0ee34df0 | |||
| 7330bdebff | |||
| 3fb5bd96e8 | |||
| 28c8ac2c4a | |||
| b9d8d54be2 | |||
| e5636247a1 | |||
| 728ece64e4 | |||
| eecdbbf315 | |||
| 6b1e9b0cd4 | |||
| 864ea90d6d | |||
| 7442e32e87 | |||
| abfc156859 | |||
| 857f80ef77 | |||
| 3efe930987 | |||
| a77f82dacf | |||
| ed9e1428d1 | |||
| 730440dcf4 | |||
| c6465439d0 | |||
| 25488a1120 | |||
| 602a3bd3de | |||
| d57ce0312a | |||
| bdff56f737 | |||
| 5b2f2af6cd | |||
| 6a37f9898e | |||
| 76f9954066 | |||
| ce3b9d2f59 | |||
| f96b8a1c67 | |||
| 5d4abb97b2 | |||
| 7ffe38cebf | |||
| 0fc189a4b4 | |||
| 013de6d5d4 | |||
| 41a19bffee | |||
| ee8f17db89 | |||
| e7fa4cde7b | |||
| 51214dd2d1 | |||
| e6499fb8a4 | |||
| d5c7462a6a | |||
| 4d4867503c | |||
| 71037bf26c | |||
| db4e2c4cd4 | |||
| 6b25f55ec9 | |||
| 0bd4dca7be | |||
| 9b1cc0bb86 | |||
| edf55404ff | |||
| 36f74f56d8 | |||
| 51294319af | |||
| c89fa51c0e | |||
| e7198624aa | |||
| 971b916b44 | |||
| 65a90d2d9b | |||
| 379a3fcee3 | |||
| ed0a48b2c1 | |||
| a97718eaed | |||
| 5ee96bbcf1 | |||
| 158eb2af96 | |||
| 42e5f933b6 | |||
| 08066a2ab2 | |||
| 8ee37654e5 | |||
| 5fbf4638ab | |||
| c7db26dab9 | |||
| 0f73fa60b9 | |||
| b174b543d8 | |||
| 233f8b265a | |||
| aa5916d94c | |||
| 2db5aef32e | |||
| 0e0bbd9922 | |||
| e274eece15 | |||
| 0483a5ad93 | |||
| 7c9f70acd4 | |||
| 5f98c897b4 | |||
| 9334d95009 | |||
| ede0d15765 | |||
| ff7b9d9d8f | |||
| 1998e85cf1 | |||
| e2f19c6b33 | |||
| 96942337fb | |||
| ebe9c36cb5 | |||
| 24d6ab75fc | |||
| 82403a6f38 | |||
| 41a10883b6 | |||
| 235847ecd5 | |||
| 8102409895 | |||
| 0710e4944f | |||
| 1b6b3b66df | |||
| aafe8337df | |||
| e3caf032d7 | |||
| 25c950b63b | |||
| d1ac630093 | |||
| 193cb51042 | |||
| 05636776e3 | |||
| 3bcd0f23ff | |||
| 15378769f2 | |||
| 5e9d6beaa3 | |||
| a43aa9fbf1 | |||
| 3f4a9df9f7 | |||
| 9f5ac013a3 | |||
| 77da718f1b | |||
| 652413ce70 | |||
| 29b3c5dfc6 | |||
| 28ac492db3 | |||
| fa0016a8a2 | |||
| 0f678757a4 | |||
| 8895425e41 | |||
| f76c4edf01 | |||
| 6e1688f7ba | |||
| 9f3a4cbbcf | |||
| 590a9dc30a | |||
| 31ab685057 | |||
| 2dfed6304e | |||
| 823640c16d | |||
| 06a8eb06f3 | |||
| 4f88b83de9 | |||
| 6fcf292760 | |||
| f23808515b | |||
| fd9233c1b9 | |||
| 759e616eb6 | |||
| a0815efa5d | |||
| 25d981659b | |||
| ce7c5de2e9 | |||
| bfb99b7e1d | |||
| 90e7406e29 | |||
| 2f31445ff8 | |||
| 0b420fab17 | |||
| a413d15875 | |||
| ba0f2d978b | |||
| 3c60db1a84 | |||
| a00bff10e5 | |||
| a837a7c721 | |||
| 19cb367592 | |||
| dfbde06b40 | |||
| a1b2b00736 | |||
| 0ec77cdea6 | |||
| acbd69f7ee | |||
| 47e4397538 | |||
| 08e9a32e5f | |||
| 9a8544daf6 | |||
| 0a1c3118ba | |||
| 782e32e3e4 | |||
| 577efa89cd | |||
| c644877218 | |||
| a63fd3a6a8 | |||
| 53a09b3886 | |||
| 07b9d5e9df | |||
| c9c9a4a76b | |||
| a5be87d234 | |||
| a70818530e | |||
| 6279184e14 | |||
| 11a788e420 | |||
| c0aac4e36f | |||
| 7a27c83bc0 | |||
| efdbd1bd1d | |||
| 28e5849960 | |||
| 761a10f8d5 | |||
| aebc072dd4 | |||
| 3c78500c9f | |||
| 624feb24f2 | |||
| a1f3fe6617 | |||
| 4ba46b8618 | |||
| 58ce5122b2 | |||
| 38a69deafa | |||
| f47b4fec8f | |||
| ec5dc1fab7 | |||
| 5ce1ff33ce | |||
| 98c5ac4358 | |||
| c1e725bbd1 | |||
| f10763ebc0 | |||
| 5eb9a4f631 | |||
| e2b27417dc | |||
| 8ebaf4bd74 | |||
| 7aaf82deb6 | |||
| 83fffcb97a | |||
| a5a7dd624f | |||
| b41b2b2e18 | |||
| 4acc62f2c4 | |||
| e32b3de09e | |||
| 7082e945b2 | |||
| ab4ec7a21c | |||
| 4e4fabd197 | |||
| 7ca837ef2c | |||
| 8ef741eeca | |||
| 764d330c3e | |||
| a95a356322 | |||
| dfc8d1d7f5 | |||
| fdf33244b5 | |||
| 3b6aa4f70c | |||
| a7695377d7 | |||
| dc606a57a7 | |||
| a82e15de84 | |||
| 80e5c694d3 | |||
| 39790f906c | |||
| aa083d663b | |||
| 2a2176186d | |||
| ae4ce123e5 | |||
| 8f71903132 | |||
| b269650d8a | |||
| 684424a6e6 | |||
| 4974d22b8d | |||
| 472b07b8e7 | |||
| de5c64fa7c | |||
| 98d135fea3 | |||
| 27ebbdd703 | |||
| 79405669f9 | |||
| 82d0300757 | |||
| 905b88c69c | |||
| 155771cb4b | |||
| a1b52cb282 | |||
| e29585b206 | |||
| 7e00410338 | |||
| 270cd52103 | |||
| a3b0030e04 | |||
| 824fef6892 | |||
| b70d618c04 | |||
| 256731fd39 | |||
| b1f5a038f3 | |||
| d088fbebac | |||
| 4947703a0b | |||
| 066bfe30b0 | |||
| ce296e211f | |||
| e719c59c0e | |||
| 82bef99867 | |||
| b954cb004c | |||
| c5a229c2f1 | |||
| 93289de65e | |||
| b207d05e48 | |||
| e28eb21659 | |||
| 5dce0f2a1a | |||
| 90c15466d8 | |||
| c78f073a41 | |||
| cb5639ca1c | |||
| 2ad02f9c91 | |||
| fa0b936d59 | |||
| fa10d60b15 | |||
| 8ded044775 | |||
| b7f4097043 | |||
| 5f38edeefd | |||
| 74d357b1c2 | |||
| 839b58679d | |||
| 79efe4f22b | |||
| 18a28dde45 | |||
| 94776f1bfa | |||
| 9a13b1108f | |||
| 52b3120916 | |||
| 3a1ade2544 | |||
| 1ab256742d | |||
| 3a98e73a54 | |||
| bdeb5d0bf1 | |||
| 9b62fec54a | |||
| cfd46e76c6 | |||
| 1c4b2c0b8d | |||
| e262d58d6c | |||
| 97eb0e4473 | |||
| 4f6e4f8d86 | |||
| 814a6297ec | |||
| 4d7f209923 | |||
| 1d8d52cdd9 | |||
| c5609f6dc5 | |||
| e008e04db1 | |||
| 6b23bd16da | |||
| 20a93a61db | |||
| e82aee6895 | |||
| 04224a6dc7 | |||
| 90ace35a9d | |||
| 414fcf2584 | |||
| c81672b56d | |||
| eb97871058 | |||
| d07b03422d | |||
| 960dfcae59 | |||
| 9eb88a59dd | |||
| 9681639337 | |||
| 115707c2b5 | |||
| d9613f122f | |||
| ade2c99109 | |||
| ee7d0bbfcf | |||
| 646f774a0f | |||
| 83bfad45a5 | |||
| 408b3ee93f | |||
| 06b2987aad | |||
| 227164c9b6 | |||
| ff04a96ba2 | |||
| b006c3f9ff | |||
| 866bbb05f7 | |||
| b98bc5cb98 | |||
| b8e91060b1 | |||
| 7a6ad10586 | |||
| e73eb478c4 | |||
| 91e6e56014 | |||
| 250611b579 | |||
| 5ccfa40292 | |||
| bd538db810 | |||
| e205c062b1 | |||
| 9cb75a10bf | |||
| c18481fab8 | |||
| c22d036916 | |||
| cac73cf1ec | |||
| d4f1027e54 | |||
| a4ea4e2b1a | |||
| d86801bed7 | |||
| a3e2211c5f | |||
| 8b080d52ff | |||
| 68575baff3 | |||
| 7e71efabf9 | |||
| 4caa3de6db | |||
| b88bb98ab7 | |||
| fc27206268 | |||
| 929b5b3210 | |||
| 9b4e4b7f64 | |||
| ff71f55265 | |||
| 8f89ca5780 | |||
| 32418eeee0 | |||
| 0469922b5f | |||
| 1cbda6fabd | |||
| eef0d3043c | |||
| 1f77f14fb9 | |||
| 574111a94e | |||
| 66b74e5aa5 | |||
| 81efab0cbb | |||
| 9717c4b19c | |||
| d6d18a2b7b | |||
| 8d8e51b073 | |||
| 2d73ec85ea | |||
| b61b21d7ee | |||
| a45dbb214b | |||
| f973e5f968 | |||
| 3e1eb36d77 | |||
| 3677e0c1aa | |||
| 133e38fdbb | |||
| 7c5074ce82 | |||
| a7029b542e | |||
| ff66e9daa8 | |||
| b12cc0679f | |||
| cfd4b8a3f5 | |||
| ee7bb53ccd | |||
| 5f074a6bb0 | |||
| 4f02d0074a | |||
| 1605660ad2 | |||
| 62415470ae | |||
| e35e70f83d | |||
| 8a073e4364 | |||
| 8de9100136 | |||
| b53af0f2a6 | |||
| 5b860c69a2 | |||
| 37b0d3adad | |||
| da2e9d519b | |||
| 6cfed595c9 | |||
| df25d33379 | |||
| 9fade08632 | |||
| e53eb2a208 | |||
| 2998a4cb30 | |||
| 0b8749c892 | |||
| e1882ddaef | |||
| bb2e1de1c2 | |||
| 4c4fc6d629 | |||
| 217d839ded | |||
| cd554c4287 | |||
| 15fac63956 | |||
| dc842aaa36 | |||
| ef967ae424 | |||
| 8279060aca | |||
| 2ab17fbdeb | |||
| 4a45541511 | |||
| 9b93e7b472 | |||
| aafc6872e4 | |||
| d073c81248 | |||
| 74aa973e8e | |||
| 9c1c9d8954 | |||
| 40e8488c8d | |||
| 9a57b4bc0b | |||
| ba541f01b6 | |||
| 64156342ad | |||
| ebc09cc3ad | |||
| 5af6a32866 | |||
| cbfbe43604 | |||
| f0040e7daf | |||
| 893139fd12 | |||
| f5d810b4f3 | |||
| e5ee31453a | |||
| 05297cc0a8 | |||
| 66e46b4859 | |||
| 4768a4768b | |||
| b4918e7d8a | |||
| 65e9de16c2 | |||
| 1e8b6e0087 | |||
| 9d649ad7a3 | |||
| ff48f6f0e7 | |||
| f5191afd30 | |||
| a6a14f2155 | |||
| bf2c42e8ee | |||
| b31c53a372 | |||
| f9d14c4cd7 | |||
| 79c8b9fcae | |||
| 33d83d755a | |||
| 4a2ac9676a | |||
| 30354dc0d2 | |||
| e906a85cc3 | |||
| 7b0e78bf83 | |||
| 24607ba326 | |||
| eb8f1fef79 | |||
| 5a0cf2cda0 | |||
| 58dd0d506e | |||
| 22a09b7d40 | |||
| 020705cca0 | |||
| e5eddec0b7 | |||
| 7269154b44 | |||
| 1e65c37e97 | |||
| b3eacbd5da | |||
| 56f0dc64ed | |||
| f4075b7127 | |||
| 1b696dcde0 | |||
| 0deddeed2f | |||
| 8c75f05721 | |||
| fc33a35857 | |||
| 5725c1850f | |||
| c7d4465070 | |||
| b4eed40953 | |||
| 08a491e16d | |||
| 53a26c87d6 | |||
| a377868a50 | |||
| 7e343b7304 | |||
| 4d2984c136 | |||
| 6719aa5ec5 | |||
| d9dcb97bbe | |||
| 51bced46a7 | |||
| a893680091 | |||
| 67a75acaff | |||
| 6060234de5 | |||
| 892173b3f3 | |||
| bc7a77f14d | |||
| a2fb18da50 | |||
| cdd7fe6a48 | |||
| fe7bf6425a | |||
| 495a02bb5a | |||
| a27c1faa9f | |||
| 6b4ae63ac8 | |||
| 4b6c90cb17 | |||
| 543397a50d | |||
| 0047b06264 | |||
| 134462ae28 | |||
| 52a027398e | |||
| e71ac727e3 | |||
| 8c5dd5e5eb | |||
| 0078652172 | |||
| b4de89cc95 | |||
| 750f660990 | |||
| 7e3c5c34d6 | |||
| 9a093f57a1 | |||
| 69d44a56fe | |||
| 41391dfd9d | |||
| 4cbc5af3a6 | |||
| bc936e8ac8 | |||
| bf6527407d | |||
| 2424069da2 | |||
| 8d1293da86 | |||
| 79f0a612c4 | |||
| 56dc43f3d2 | |||
| 217c5d8208 |
@@ -1128,5 +1128,12 @@
|
||||
</FEEDBACK>
|
||||
</PHP_SETTING>
|
||||
</PHP_SETTINGS>
|
||||
<CUSTOM_CHECKS>
|
||||
<CUSTOM_CHECK file="question/engine/upgrade/upgradelib.php" function="quiz_attempts_upgraded" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="quizattemptsupgradedmessage" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
</CUSTOM_CHECKS>
|
||||
</MOODLE>
|
||||
</COMPATIBILITY_MATRIX>
|
||||
|
||||
@@ -41,8 +41,11 @@ if ($hassiteconfig or has_any_capability($capabilities, $systemcontext)) { // sp
|
||||
}
|
||||
|
||||
|
||||
// calendar
|
||||
// Calendar settings.
|
||||
$temp = new admin_settingpage('calendar', new lang_string('calendarsettings','admin'));
|
||||
|
||||
$temp->add(new admin_setting_configselect('calendartype', new lang_string('calendartype', 'admin'),
|
||||
new lang_string('calendartype_desc', 'admin'), 'gregorian', \core_calendar\type_factory::get_list_of_calendar_types()));
|
||||
$temp->add(new admin_setting_special_adminseesall());
|
||||
//this is hacky because we do not want to include the stuff from calendar/lib.php
|
||||
$temp->add(new admin_setting_configselect('calendar_site_timeformat', new lang_string('pref_timeformat', 'calendar'),
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
/**
|
||||
* Steps definitions related with administration.
|
||||
*
|
||||
* @package core
|
||||
* @package core_admin
|
||||
* @category test
|
||||
* @copyright 2013 David Monllaó
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
@@ -35,7 +35,7 @@ use Behat\Behat\Context\Step\Given as Given,
|
||||
/**
|
||||
* Site administration level steps definitions.
|
||||
*
|
||||
* @package core
|
||||
* @package core_admin
|
||||
* @category test
|
||||
* @copyright 2013 David Monllaó
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@core @core_admin @_only_local
|
||||
@core @core_admin @_only_local @_file_upload
|
||||
Feature: Upload users
|
||||
In order to add users to the system
|
||||
As an admin
|
||||
|
||||
@@ -183,8 +183,8 @@ if ($options['install']) {
|
||||
mtrace("Acceptance tests site dropped");
|
||||
} else if ($options['enable']) {
|
||||
behat_util::start_test_mode();
|
||||
$runtestscommand = behat_command::get_behat_command() . ' --config '
|
||||
. $CFG->behat_dataroot . DIRECTORY_SEPARATOR . 'behat' . DIRECTORY_SEPARATOR . 'behat.yml';
|
||||
$runtestscommand = behat_command::get_behat_command(true) .
|
||||
' --config ' . behat_config_manager::get_behat_cli_config_filepath();
|
||||
mtrace("Acceptance tests environment enabled, to run the tests use:\n " . $runtestscommand);
|
||||
} else if ($options['disable']) {
|
||||
behat_util::stop_test_mode();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@tool @tool_behat @_only_local
|
||||
@tool @tool_behat
|
||||
Feature: Set up contextual data for tests
|
||||
In order to write tests quickly
|
||||
As a developer
|
||||
@@ -90,10 +90,10 @@ Feature: Set up contextual data for tests
|
||||
And I expand "Users" node
|
||||
And I follow "Permissions"
|
||||
And I select "Student (1)" from "Advanced role override"
|
||||
Then the "mod/forum:editanypost" field should match "1" value
|
||||
Then "mod/forum:editanypost" capability has "Allow" permission
|
||||
And I press "Cancel"
|
||||
And I select "Teacher (1)" from "Advanced role override"
|
||||
And the "mod/forum:replynews" field should match "-1" value
|
||||
And "mod/forum:replynews" capability has "Prevent" permission
|
||||
And I press "Cancel"
|
||||
|
||||
Scenario: Add course enrolments
|
||||
|
||||
@@ -25,10 +25,10 @@ Feature: Edit capabilities
|
||||
| moodle/grade:managesharedforms | Prevent |
|
||||
| moodle/course:request | Prohibit |
|
||||
When I follow "Edit Teacher role"
|
||||
Then the "block/mnet_hosts:myaddinstance" field should match "1" value
|
||||
And the "moodle/community:add" field should match "0" value
|
||||
And the "moodle/grade:managesharedforms" field should match "-1" value
|
||||
And the "moodle/course:request" field should match "-1000" value
|
||||
Then "block/mnet_hosts:myaddinstance" capability has "Allow" permission
|
||||
And "moodle/community:add" capability has "Not set" permission
|
||||
And "moodle/grade:managesharedforms" capability has "Prevent" permission
|
||||
And "moodle/course:request" capability has "Prohibit" permission
|
||||
|
||||
@javascript
|
||||
Scenario: Course capabilities overrides
|
||||
@@ -41,9 +41,9 @@ Feature: Edit capabilities
|
||||
| mod/forum:editanypost | Prevent |
|
||||
| mod/forum:addquestion | Allow |
|
||||
When I select "Student (3)" from "Advanced role override"
|
||||
Then the "mod/forum:deleteanypost" field should match "-1000" value
|
||||
And the "mod/forum:editanypost" field should match "-1" value
|
||||
And the "mod/forum:addquestion" field should match "1" value
|
||||
Then "mod/forum:deleteanypost" capability has "Prohibit" permission
|
||||
And "mod/forum:editanypost" capability has "Prevent" permission
|
||||
And "mod/forum:addquestion" capability has "Allow" permission
|
||||
|
||||
@javascript
|
||||
Scenario: Module capabilities overrides
|
||||
@@ -60,6 +60,6 @@ Feature: Edit capabilities
|
||||
| mod/forum:editanypost | Prevent |
|
||||
| mod/forum:addquestion | Allow |
|
||||
When I select "Student (3)" from "Advanced role override"
|
||||
Then the "mod/forum:deleteanypost" field should match "-1000" value
|
||||
And the "mod/forum:editanypost" field should match "-1" value
|
||||
And the "mod/forum:addquestion" field should match "1" value
|
||||
Then "mod/forum:deleteanypost" capability has "Prohibit" permission
|
||||
And "mod/forum:editanypost" capability has "Prevent" permission
|
||||
And "mod/forum:addquestion" capability has "Allow" permission
|
||||
|
||||
@@ -0,0 +1,173 @@
|
||||
@tool_behat
|
||||
Feature: Verify that all form fields values can be get and set
|
||||
In order to use behat steps definitions
|
||||
As a test writer
|
||||
I need to verify it all works in real moodle forms
|
||||
|
||||
Background:
|
||||
Given the following "courses" exists:
|
||||
| fullname | shortname | category |
|
||||
| Course 1 | C1 | 0 |
|
||||
And the following "users" exists:
|
||||
| username | email | firstname | lastname |
|
||||
| student1 | s1@asd.com | Student | 1 |
|
||||
| student2 | s2@asd.com | Student | 2 |
|
||||
| student3 | s3@asd.com | Student | 3 |
|
||||
And the following "course enrolments" exists:
|
||||
| user | course | role |
|
||||
| student1 | C1 | student |
|
||||
| student2 | C1 | student |
|
||||
| student3 | C1 | student |
|
||||
| admin | C1 | editingteacher |
|
||||
And the following "groups" exists:
|
||||
| name | description | course | idnumber |
|
||||
| Group 1 | G1 description | C1 | G1 |
|
||||
| Group 2 | G1 description | C1 | G2 |
|
||||
And the following "group members" exists:
|
||||
| user | group |
|
||||
| student1 | G1 |
|
||||
| student2 | G1 |
|
||||
| student2 | G2 |
|
||||
| student3 | G2 |
|
||||
And the following "activities" exists:
|
||||
| activity | course | idnumber | name | intro | firstpagetitle | wikimode | visible |
|
||||
| wiki | C1 | wiki1 | Test this one | Test this one | Test this one | collaborative | 0 |
|
||||
And I log in as "admin"
|
||||
And I expand "Site administration" node
|
||||
And I expand "Appearance" node
|
||||
And I follow "Manage tags"
|
||||
# Select (multi-select) - We will check "I fill in...".
|
||||
And I fill in "otagsadd" with "OT1, OT2, OT3, OT4, OT5"
|
||||
And I press "Add official tags"
|
||||
And I am on homepage
|
||||
And I follow "Course 1"
|
||||
And I turn editing mode on
|
||||
And I follow "Test this one"
|
||||
And I press "Create page"
|
||||
# Select (multi-select) - Checking "the select box should contain".
|
||||
And the "tags[officialtags][]" select box should contain "OT1"
|
||||
And the "tags[officialtags][]" select box should contain "OT2"
|
||||
And the "tags[officialtags][]" select box should contain "OT3"
|
||||
And the "tags[officialtags][]" select box should contain "OT4"
|
||||
And the "tags[officialtags][]" select box should contain "OT5"
|
||||
And the "tags[officialtags][]" select box should contain "OT1, OT2, OT3, OT4, OT5"
|
||||
And the "tags[officialtags][]" select box should contain "OT5, OT4, OT3, OT2, OT1"
|
||||
And the "tags[officialtags][]" select box should not contain "OT6"
|
||||
And the "tags[officialtags][]" select box should not contain "OT7"
|
||||
And the "tags[officialtags][]" select box should not contain "OT6, OT7"
|
||||
# Text (textarea & editor) & Select (multi-select) - Checking "I fill the moodle form with".
|
||||
When I fill the moodle form with:
|
||||
| HTML format | Student page contents to be tagged |
|
||||
| tags[officialtags][] | OT1, OT3, OT5 |
|
||||
And I press "Save"
|
||||
Then I should see "Student page contents to be tagged" in the "region-main" "region"
|
||||
And I should see "OT1" in the ".wiki-tags" "css_element"
|
||||
And I should see "OT3" in the ".wiki-tags" "css_element"
|
||||
And I should see "OT5" in the ".wiki-tags" "css_element"
|
||||
And I should not see "OT2" in the ".wiki-tags" "css_element"
|
||||
And I should not see "OT4" in the ".wiki-tags" "css_element"
|
||||
And I follow "Edit"
|
||||
# Select (multi-select) - Checking "I select from".
|
||||
And I select "OT2, OT4" from "tags[officialtags][]"
|
||||
And I press "Save"
|
||||
And I should see "OT2" in the ".wiki-tags" "css_element"
|
||||
And I should see "OT4" in the ".wiki-tags" "css_element"
|
||||
And I should not see "OT1" in the ".wiki-tags" "css_element"
|
||||
And I should not see "OT3" in the ".wiki-tags" "css_element"
|
||||
And I should not see "OT5" in the ".wiki-tags" "css_element"
|
||||
And I follow "Edit"
|
||||
# Select (multi-select) - Checking "the field should match" and "the field does not match value".
|
||||
And the "tags[officialtags][]" field should match "OT2, OT4" value
|
||||
And the field "tags[officialtags][]" does not match value "OT4"
|
||||
And the field "tags[officialtags][]" does not match value "OT2"
|
||||
And the field "tags[officialtags][]" does not match value "OT1, OT3, OT5"
|
||||
And I press "Cancel"
|
||||
And I follow "Edit settings"
|
||||
And I expand all fieldsets
|
||||
# Checkbox - Checking "I check".
|
||||
And I check "Display description on course page"
|
||||
# Checkbox - Checking "I fill the moodle form with:".
|
||||
And I fill the moodle form with:
|
||||
| Force format | 1 |
|
||||
# Checkbox - Checking "the field should match" and "the field does not match value".
|
||||
And the "Display description on course page" field should match "1" value
|
||||
And the field "Display description on course page" does not match value ""
|
||||
And I press "Save and return to course"
|
||||
And I should see "Test this one"
|
||||
And I follow "Test this one"
|
||||
And I follow "Edit settings"
|
||||
# Checkbox - Checking "the field should match" and "the following fields match these values".
|
||||
And the following fields match these values:
|
||||
| Display description on course page | 1 |
|
||||
| Default format | HTML |
|
||||
| Wiki name | Test this one |
|
||||
And the "Force format" field should match "1" value
|
||||
# Select (simple) - Checking "I fill the moodle form with:".
|
||||
And I fill the moodle form with:
|
||||
| Default format | NWiki |
|
||||
| Display description on course page | |
|
||||
# Checkbox - Checking "I uncheck".
|
||||
And I uncheck "Force format"
|
||||
# Select (simple) - Checking "I select from".
|
||||
And I select "Separate groups" from "Group mode"
|
||||
And I press "Save and display"
|
||||
And I follow "Edit settings"
|
||||
And the following fields match these values:
|
||||
| Default format | NWiki |
|
||||
| Group mode | Separate groups |
|
||||
| Display description on course page | |
|
||||
| Force format | |
|
||||
# All fields - Checking "the following fields do not match these values".
|
||||
And the following fields do not match these values:
|
||||
| Wiki name | Test this one baby |
|
||||
| Default format | HTML |
|
||||
| Force format | 1 |
|
||||
And I press "Cancel"
|
||||
And I follow "Course 1"
|
||||
# Radio - Checking "I check" and "the field should match".
|
||||
And I add a "Choice" to section "1" and I fill the form with:
|
||||
| Choice name | Test choice name |
|
||||
| Description | Test choice description |
|
||||
| Allow choice to be updated | Yes |
|
||||
| Option 1 | one |
|
||||
| Option 2 | two |
|
||||
| Option 3 | three |
|
||||
And I follow "Test choice name"
|
||||
And I check "one"
|
||||
And I press "Save my choice"
|
||||
And the "one" field should match "1" value
|
||||
And the "two" field should match "" value
|
||||
|
||||
Scenario: with JS disabled all form fields getters and setters works as expected
|
||||
|
||||
@javascript
|
||||
Scenario: with JS enabled all form fields getters and setters works as expected
|
||||
Then I follow "Course 1"
|
||||
And I expand "Users" node
|
||||
And I follow "Groups"
|
||||
# Select (multi-select & AJAX) - Checking "I select from" and "select box should contain".
|
||||
And I select "Group 2" from "groups"
|
||||
And the "members" select box should contain "Student 2"
|
||||
And the "members" select box should contain "Student 3"
|
||||
And the "members" select box should not contain "Student 1"
|
||||
And I select "Group 1" from "groups"
|
||||
And the "members" select box should contain "Student 1"
|
||||
And the "members" select box should contain "Student 2"
|
||||
And the "members" select box should not contain "Student 3"
|
||||
# Checkbox (AJAX) - Checking "I check" and "I fill the moodle form with".
|
||||
And I follow "Course 1"
|
||||
And I add a "Lesson" to section "1"
|
||||
And I fill the moodle form with:
|
||||
| Name | Test lesson |
|
||||
| available[enabled] | 1 |
|
||||
And I check "deadline[enabled]"
|
||||
# Checkbox (AJAX) - Checking "field should match" before saving.
|
||||
And the "available[enabled]" field should match "1" value
|
||||
And the "available[day]" "field" should be enabled
|
||||
And the "deadline[enabled]" field should match "1" value
|
||||
And I press "Save and display"
|
||||
And I follow "Edit settings"
|
||||
And the "available[enabled]" field should match "1" value
|
||||
And the "available[day]" "field" should be enabled
|
||||
And the "deadline[enabled]" field should match "1" value
|
||||
And I press "Cancel"
|
||||
@@ -1,4 +1,4 @@
|
||||
@tool @tool_uploadcourse @_only_local
|
||||
@tool @tool_uploadcourse @_only_local @_file_upload
|
||||
Feature: An admin can create courses using a CSV file
|
||||
In order to create courses using a CSV file
|
||||
As an admin
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@tool @tool_uploadcourse @_only_local
|
||||
@tool @tool_uploadcourse @_only_local @_file_upload
|
||||
Feature: An admin can update courses using a CSV file
|
||||
In order to update courses using a CSV file
|
||||
As an admin
|
||||
|
||||
@@ -68,7 +68,7 @@ if ($usersettingsform->is_cancelled()) {
|
||||
//TODO: assign capability
|
||||
|
||||
//display successful notification
|
||||
$notification = $OUTPUT->notification(get_string('usersettingssaved', 'webservice'), 'success');
|
||||
$notification = $OUTPUT->notification(get_string('usersettingssaved', 'webservice'), 'notifysuccess');
|
||||
}
|
||||
|
||||
echo $OUTPUT->header();
|
||||
|
||||
@@ -422,6 +422,7 @@ class auth_plugin_cas extends auth_plugin_ldap {
|
||||
|
||||
// Test for group creator
|
||||
if (!empty($this->config->groupecreators)) {
|
||||
$ldapconnection = $this->ldap_connect();
|
||||
if ($this->config->memberattribute_isdn) {
|
||||
if(!($userid = $this->ldap_find_userdn($ldapconnection, $extusername))) {
|
||||
return false;
|
||||
|
||||
@@ -399,6 +399,9 @@ class auth_plugin_db extends auth_plugin_base {
|
||||
if (empty($user->lang)) {
|
||||
$user->lang = $CFG->lang;
|
||||
}
|
||||
if (empty($user->calendartype)) {
|
||||
$user->calendartype = $CFG->calendartype;
|
||||
}
|
||||
$user->timecreated = time();
|
||||
$user->timemodified = $user->timecreated;
|
||||
if ($collision = $DB->get_record_select('user', "username = :username AND mnethostid = :mnethostid AND auth <> :auth", array('username'=>$user->username, 'mnethostid'=>$CFG->mnet_localhost_id, 'auth'=>$this->authtype), 'id,username,auth')) {
|
||||
|
||||
@@ -90,6 +90,9 @@ class auth_plugin_email extends auth_plugin_base {
|
||||
require_once($CFG->dirroot.'/user/lib.php');
|
||||
|
||||
$user->password = hash_internal_user_password($user->password);
|
||||
if (empty($user->calendartype)) {
|
||||
$user->calendartype = $CFG->calendartype;
|
||||
}
|
||||
|
||||
$user->id = user_create_user($user, false);
|
||||
|
||||
|
||||
+12
-3
@@ -944,6 +944,9 @@ class auth_plugin_ldap extends auth_plugin_base {
|
||||
if (empty($user->lang)) {
|
||||
$user->lang = $CFG->lang;
|
||||
}
|
||||
if (empty($user->calendartype)) {
|
||||
$user->calendartype = $CFG->calendartype;
|
||||
}
|
||||
|
||||
$id = user_create_user($user, false);
|
||||
echo "\t"; print_string('auth_dbinsertuser', 'auth_db', array('name'=>$user->username, 'id'=>$id)); echo "\n";
|
||||
@@ -1179,6 +1182,7 @@ class auth_plugin_ldap extends auth_plugin_base {
|
||||
return false;
|
||||
}
|
||||
|
||||
$success = true;
|
||||
$user_info_result = ldap_read($ldapconnection, $user_dn, '(objectClass=*)', $search_attribs);
|
||||
if ($user_info_result) {
|
||||
$user_entry = ldap_get_entries_moodle($ldapconnection, $user_info_result);
|
||||
@@ -1233,8 +1237,10 @@ class auth_plugin_ldap extends auth_plugin_base {
|
||||
if ($nuvalue !== $ldapvalue) {
|
||||
// This might fail due to schema validation
|
||||
if (@ldap_modify($ldapconnection, $user_dn, array($ldapkey => $nuvalue))) {
|
||||
$changed = true;
|
||||
continue;
|
||||
} else {
|
||||
$success = false;
|
||||
error_log($this->errorlogtag.get_string ('updateremfail', 'auth_ldap',
|
||||
array('errno'=>ldap_errno($ldapconnection),
|
||||
'errstring'=>ldap_err2str(ldap_errno($ldapconnection)),
|
||||
@@ -1253,6 +1259,7 @@ class auth_plugin_ldap extends auth_plugin_base {
|
||||
$changed = true;
|
||||
continue;
|
||||
} else {
|
||||
$success = false;
|
||||
error_log($this->errorlogtag.get_string ('updateremfail', 'auth_ldap',
|
||||
array('errno'=>ldap_errno($ldapconnection),
|
||||
'errstring'=>ldap_err2str(ldap_errno($ldapconnection)),
|
||||
@@ -1270,6 +1277,7 @@ class auth_plugin_ldap extends auth_plugin_base {
|
||||
$changed = true;
|
||||
continue;
|
||||
} else {
|
||||
$success = false;
|
||||
error_log($this->errorlogtag.get_string ('updateremfail', 'auth_ldap',
|
||||
array('errno'=>ldap_errno($ldapconnection),
|
||||
'errstring'=>ldap_err2str(ldap_errno($ldapconnection)),
|
||||
@@ -1283,6 +1291,7 @@ class auth_plugin_ldap extends auth_plugin_base {
|
||||
}
|
||||
|
||||
if ($ambiguous and !$changed) {
|
||||
$success = false;
|
||||
error_log($this->errorlogtag.get_string ('updateremfailamb', 'auth_ldap',
|
||||
array('key'=>$key,
|
||||
'ouvalue'=>$ouvalue,
|
||||
@@ -1292,12 +1301,11 @@ class auth_plugin_ldap extends auth_plugin_base {
|
||||
}
|
||||
} else {
|
||||
error_log($this->errorlogtag.get_string ('usernotfound', 'auth_ldap'));
|
||||
$this->ldap_close();
|
||||
return false;
|
||||
$success = false;
|
||||
}
|
||||
|
||||
$this->ldap_close();
|
||||
return true;
|
||||
return $success;
|
||||
|
||||
}
|
||||
|
||||
@@ -1526,6 +1534,7 @@ class auth_plugin_ldap extends auth_plugin_base {
|
||||
array_push($contexts, $this->config->create_context);
|
||||
}
|
||||
|
||||
$ldap_cookie = '';
|
||||
$ldap_pagedresults = ldap_paged_results_supported($this->config->ldap_version);
|
||||
foreach ($contexts as $context) {
|
||||
$context = trim($context);
|
||||
|
||||
@@ -268,10 +268,8 @@ class auth_ldap_plugin_testcase extends advanced_testcase {
|
||||
$sink->close();
|
||||
|
||||
// Check that the event is valid.
|
||||
$this->assertCount(2, $events);
|
||||
$event = $events[0];
|
||||
$this->assertInstanceOf('\core\event\user_updated', $event);
|
||||
$event = $events[1];
|
||||
$this->assertCount(1, $events);
|
||||
$event = reset($events);
|
||||
$this->assertInstanceOf('\core\event\user_loggedin', $event);
|
||||
$this->assertEquals('user', $event->objecttable);
|
||||
$this->assertEquals('2', $event->objectid);
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
/**
|
||||
* Basic authentication steps definitions.
|
||||
*
|
||||
* @package core
|
||||
* @package core_auth
|
||||
* @category test
|
||||
* @copyright 2012 David Monllaó
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
@@ -34,7 +34,7 @@ use Behat\Behat\Context\Step\When as When;
|
||||
/**
|
||||
* Log in log out steps definitions.
|
||||
*
|
||||
* @package core
|
||||
* @package core_auth
|
||||
* @category test
|
||||
* @copyright 2012 David Monllaó
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
@@ -48,12 +48,45 @@ class behat_auth extends behat_base {
|
||||
*/
|
||||
public function i_log_in_as($username) {
|
||||
|
||||
return array(new Given('I am on homepage'),
|
||||
// Running this step using the API rather than a chained step because
|
||||
// we need to see if the 'Log in' link is available or we need to click
|
||||
// the dropdown to expand the navigation bar before.
|
||||
$this->getSession()->visit($this->locate_path('/'));
|
||||
|
||||
// Generic steps (we will prefix them later expanding the navigation dropdown if necessary).
|
||||
$steps = array(
|
||||
new Given('I follow "' . get_string('login') . '"'),
|
||||
new Given('I fill in "' . get_string('username') . '" with "' . $this->escape($username) . '"'),
|
||||
new Given('I fill in "' . get_string('password') . '" with "'. $this->escape($username) . '"'),
|
||||
new Given('I press "' . get_string('login') . '"')
|
||||
);
|
||||
|
||||
// If Javascript is disabled we have enough with these steps.
|
||||
if (!$this->running_javascript()) {
|
||||
return $steps;
|
||||
}
|
||||
|
||||
// Wait for the homepage to be ready.
|
||||
$this->getSession()->wait(self::TIMEOUT * 1000, self::PAGE_READY_JS);
|
||||
|
||||
// Checking if we need to click the navbar button to show the navigation menu, it
|
||||
// is hidden by default when using clean theme and a medium or small size screen size.
|
||||
|
||||
// The DOM and the JS should be all ready and loaded. Running without spinning
|
||||
// as this is a widely used step and we can not spend time here trying to see
|
||||
// a DOM node that is not always there (at the moment clean is not even the
|
||||
// default theme...).
|
||||
$navbuttonjs = "return (
|
||||
Y.one('.btn-navbar') &&
|
||||
Y.one('.btn-navbar').getComputedStyle('display') !== 'none'
|
||||
)";
|
||||
|
||||
// Adding an extra click we need to show the 'Log in' link.
|
||||
if ($this->getSession()->getDriver()->evaluateScript($navbuttonjs)) {
|
||||
array_unshift($steps, new Given('I click on ".btn-navbar" "css_element"'));
|
||||
}
|
||||
|
||||
return $steps;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,8 +4,14 @@ Feature: Authentication
|
||||
As a user
|
||||
I need to log into the system
|
||||
|
||||
Scenario: Log in with the predefined admin user
|
||||
Scenario: Log in with the predefined admin user with Javascript disabled
|
||||
Given I log in as "admin"
|
||||
Then I should see "You are logged in as Admin User"
|
||||
|
||||
@javascript
|
||||
Scenario: Log in with the predefined admin user with Javascript enabled
|
||||
Given I log in as "admin"
|
||||
Then I should see "You are logged in as Admin User"
|
||||
|
||||
Scenario: Log in as an existing admin user filling the form
|
||||
Given the following "users" exists:
|
||||
|
||||
+1
-1
@@ -176,7 +176,7 @@ if ($backup->get_stage() == backup_ui::STAGE_FINAL) {
|
||||
// Display a notification and a continue button
|
||||
if ($warnings) {
|
||||
echo $OUTPUT->box_start();
|
||||
echo $OUTPUT->notification(get_string('warning'), 'notifywarning');
|
||||
echo $OUTPUT->notification(get_string('warning'), 'notifyproblem');
|
||||
echo html_writer::start_tag('ul', array('class'=>'list'));
|
||||
foreach ($warnings as $warning) {
|
||||
echo html_writer::tag('li', $warning);
|
||||
|
||||
@@ -216,7 +216,7 @@ abstract class backup_questions_activity_structure_step extends backup_activity_
|
||||
|
||||
$qas = new backup_nested_element($nameprefix . 'question_attempts');
|
||||
$qa = new backup_nested_element($nameprefix . 'question_attempt', array('id'), array(
|
||||
'slot', 'behaviour', 'questionid', 'maxmark', 'minfraction', 'maxfraction',
|
||||
'slot', 'behaviour', 'questionid', 'variant', 'maxmark', 'minfraction', 'maxfraction',
|
||||
'flagged', 'questionsummary', 'rightanswer', 'responsesummary',
|
||||
'timemodified'));
|
||||
|
||||
|
||||
@@ -124,6 +124,7 @@ class restore_course_task extends restore_task {
|
||||
$contents = array();
|
||||
|
||||
$contents[] = new restore_decode_content('course', 'summary');
|
||||
$contents[] = new restore_decode_content('event', 'description');
|
||||
|
||||
return $contents;
|
||||
}
|
||||
|
||||
@@ -162,7 +162,12 @@ class restore_final_task extends restore_task {
|
||||
// rules from other tasks (activities) not belonging to one module instance (cmid = 0), so are restored here
|
||||
$rules = array_merge($rules, restore_logs_processor::register_log_rules_for_course());
|
||||
|
||||
// TODO: Other logs like 'calendar', 'upload'... will go here
|
||||
// Calendar rules.
|
||||
$rules[] = new restore_log_rule('calendar', 'add', 'event.php?action=edit&id={event}', '[name]');
|
||||
$rules[] = new restore_log_rule('calendar', 'edit', 'event.php?action=edit&id={event}', '[name]');
|
||||
$rules[] = new restore_log_rule('calendar', 'edit all', 'event.php?action=edit&id={event}', '[name]');
|
||||
|
||||
// TODO: Other logs like 'upload'... will go here
|
||||
|
||||
return $rules;
|
||||
}
|
||||
|
||||
@@ -2223,6 +2223,7 @@ class restore_calendarevents_structure_step extends restore_structure_step {
|
||||
$result = $DB->record_exists_sql($sql, $arg);
|
||||
if (empty($result)) {
|
||||
$newitemid = $DB->insert_record('event', $params);
|
||||
$this->set_mapping('event', $oldid, $newitemid);
|
||||
$this->set_mapping('event_description', $oldid, $newitemid, $restorefiles);
|
||||
}
|
||||
|
||||
@@ -2832,6 +2833,12 @@ class restore_activity_grades_structure_step extends restore_structure_step {
|
||||
}
|
||||
// no need to save any grade_letter mapping
|
||||
}
|
||||
|
||||
public function after_restore() {
|
||||
// Fix grade item's sortorder after restore, as it might have duplicates.
|
||||
$courseid = $this->get_task()->get_courseid();
|
||||
grade_item::fix_duplicate_sortorder($courseid);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4062,6 +4069,9 @@ abstract class restore_questions_activity_structure_step extends restore_activit
|
||||
|
||||
$data->questionusageid = $this->get_new_parentid($nameprefix . 'question_usage');
|
||||
$data->questionid = $question->newitemid;
|
||||
if (!property_exists($data, 'variant')) {
|
||||
$data->variant = 1;
|
||||
}
|
||||
$data->timemodified = $this->apply_date_offset($data->timemodified);
|
||||
|
||||
if (!property_exists($data, 'maxfraction')) {
|
||||
|
||||
@@ -410,7 +410,7 @@ class core_backup_renderer extends plugin_renderer_base {
|
||||
}
|
||||
if (array_key_exists('warnings', $results)) {
|
||||
foreach ($results['warnings'] as $warning) {
|
||||
$output .= $this->output->notification($warning, 'notifywarning notifyproblem');
|
||||
$output .= $this->output->notification($warning, 'notifyproblem');
|
||||
}
|
||||
}
|
||||
return $output.html_writer::end_tag('div');
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
/**
|
||||
* Backup and restore actions to help behat feature files writting.
|
||||
*
|
||||
* @package core
|
||||
* @package core_backup
|
||||
* @category test
|
||||
* @copyright 2013 David Monllaó
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
@@ -35,7 +35,7 @@ use Behat\Gherkin\Node\TableNode as TableNode,
|
||||
/**
|
||||
* Backup-related steps definitions.
|
||||
*
|
||||
* @package core
|
||||
* @package core_backup
|
||||
* @category test
|
||||
* @copyright 2013 David Monllaó
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
|
||||
+9
-5
@@ -42,7 +42,14 @@ if (empty($CFG->enablebadges)) {
|
||||
print_error('badgesdisabled', 'badges');
|
||||
}
|
||||
|
||||
$url = new moodle_url('/badges/mybadges.php');
|
||||
$PAGE->set_url($url);
|
||||
|
||||
if (isguestuser()) {
|
||||
$PAGE->set_context(context_system::instance());
|
||||
echo $OUTPUT->header();
|
||||
echo $OUTPUT->box(get_string('error:guestuseraccess', 'badges'), 'notifyproblem');
|
||||
echo $OUTPUT->footer();
|
||||
die();
|
||||
}
|
||||
|
||||
@@ -56,10 +63,10 @@ if ($clearsearch) {
|
||||
|
||||
if ($hide) {
|
||||
require_sesskey();
|
||||
$DB->set_field('badge_issued', 'visible', 0, array('id' => $hide));
|
||||
$DB->set_field('badge_issued', 'visible', 0, array('id' => $hide, 'userid' => $USER->id));
|
||||
} else if ($show) {
|
||||
require_sesskey();
|
||||
$DB->set_field('badge_issued', 'visible', 1, array('id' => $show));
|
||||
$DB->set_field('badge_issued', 'visible', 1, array('id' => $show, 'userid' => $USER->id));
|
||||
} else if ($download && $hash) {
|
||||
require_sesskey();
|
||||
$badge = new badge($download);
|
||||
@@ -80,9 +87,6 @@ if ($hide) {
|
||||
$context = context_user::instance($USER->id);
|
||||
require_capability('moodle/badges:manageownbadges', $context);
|
||||
|
||||
$url = new moodle_url('/badges/mybadges.php');
|
||||
|
||||
$PAGE->set_url($url);
|
||||
$PAGE->set_context($context);
|
||||
|
||||
$title = get_string('mybadges', 'badges');
|
||||
|
||||
@@ -91,7 +91,7 @@ $namefields = get_all_user_name_fields(true, 'u');
|
||||
$sql = "SELECT b.userid, b.dateissued, b.uniquehash, $namefields
|
||||
FROM {badge_issued} b INNER JOIN {user} u
|
||||
ON b.userid = u.id
|
||||
WHERE b.badgeid = :badgeid
|
||||
WHERE b.badgeid = :badgeid AND u.deleted = 0
|
||||
ORDER BY $sortby $sorthow";
|
||||
|
||||
$totalcount = $DB->count_records('badge_issued', array('badgeid' => $badge->id));
|
||||
|
||||
+16
-7
@@ -279,7 +279,7 @@ class core_badges_renderer extends plugin_renderer_base {
|
||||
|
||||
// Outputs issued badge with actions available.
|
||||
protected function render_issued_badge(issued_badge $ibadge) {
|
||||
global $USER, $CFG, $DB;
|
||||
global $USER, $CFG, $DB, $SITE;
|
||||
$issued = $ibadge->issued;
|
||||
$userinfo = $ibadge->recipient;
|
||||
$badgeclass = $ibadge->badgeclass;
|
||||
@@ -316,11 +316,18 @@ class core_badges_renderer extends plugin_renderer_base {
|
||||
|
||||
// Recipient information.
|
||||
$datatable->data[] = array($this->output->heading(get_string('recipientdetails', 'badges'), 3), '');
|
||||
$datatable->data[] = array(get_string('name'), fullname($userinfo));
|
||||
if (empty($userinfo->backpackemail)) {
|
||||
$datatable->data[] = array(get_string('email'), obfuscate_mailto($userinfo->accountemail));
|
||||
if ($userinfo->deleted) {
|
||||
$strdata = new stdClass();
|
||||
$strdata->user = fullname($userinfo);
|
||||
$strdata->site = format_string($SITE->fullname, true, array('context' => context_system::instance()));
|
||||
$datatable->data[] = array(get_string('name'), get_string('error:userdeleted', 'badges', $strdata));
|
||||
} else {
|
||||
$datatable->data[] = array(get_string('email'), obfuscate_mailto($userinfo->backpackemail));
|
||||
$datatable->data[] = array(get_string('name'), fullname($userinfo));
|
||||
if (empty($userinfo->backpackemail)) {
|
||||
$datatable->data[] = array(get_string('email'), obfuscate_mailto($userinfo->accountemail));
|
||||
} else {
|
||||
$datatable->data[] = array(get_string('email'), obfuscate_mailto($userinfo->backpackemail));
|
||||
}
|
||||
}
|
||||
|
||||
$datatable->data[] = array($this->output->heading(get_string('issuerdetails', 'badges'), 3), '');
|
||||
@@ -663,7 +670,9 @@ class core_badges_renderer extends plugin_renderer_base {
|
||||
}
|
||||
|
||||
if (has_capability('moodle/badges:viewawarded', $context)) {
|
||||
$awarded = $DB->count_records('badge_issued', array('badgeid' => $badgeid));
|
||||
$awarded = $DB->count_records_sql('SELECT COUNT(b.userid)
|
||||
FROM {badge_issued} b INNER JOIN {user} u ON b.userid = u.id
|
||||
WHERE b.badgeid = :badgeid AND u.deleted = 0', array('badgeid' => $badgeid));
|
||||
$row[] = new tabobject('awards',
|
||||
new moodle_url('/badges/recipients.php', array('id' => $badgeid)),
|
||||
get_string('bawards', 'badges', $awarded)
|
||||
@@ -936,7 +945,7 @@ class issued_badge implements renderable {
|
||||
if ($rec) {
|
||||
// Get a recipient from database.
|
||||
$namefields = get_all_user_name_fields(true, 'u');
|
||||
$user = $DB->get_record_sql("SELECT u.id, $namefields,
|
||||
$user = $DB->get_record_sql("SELECT u.id, $namefields, u.deleted,
|
||||
u.email AS accountemail, b.email AS backpackemail
|
||||
FROM {user} u LEFT JOIN {badge_backpack} b ON u.id = b.userid
|
||||
WHERE u.id = :userid", array('userid' => $rec->userid));
|
||||
|
||||
@@ -233,7 +233,7 @@ class core_badges_badgeslib_testcase extends advanced_testcase {
|
||||
$criteria_overall = award_criteria::build(array('criteriatype' => BADGE_CRITERIA_TYPE_OVERALL, 'badgeid' => $badge->id));
|
||||
$criteria_overall->save(array('agg' => BADGE_CRITERIA_AGGREGATION_ANY));
|
||||
$criteria_overall = award_criteria::build(array('criteriatype' => BADGE_CRITERIA_TYPE_ACTIVITY, 'badgeid' => $badge->id));
|
||||
$criteria_overall->save(array('agg' => BADGE_CRITERIA_AGGREGATION_ANY, 'module_'.$this->module->id => $this->module->id));
|
||||
$criteria_overall->save(array('agg' => BADGE_CRITERIA_AGGREGATION_ANY, 'module_'.$this->module->cmid => $this->module->cmid));
|
||||
|
||||
// Set completion for forum activity.
|
||||
$c = new completion_info($this->course);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@core @core_badges @_only_local
|
||||
@core @core_badges @_only_local @_file_upload
|
||||
Feature: Add badges to the system
|
||||
In order to give badges to users for their achievements
|
||||
As an admin
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@core @core_badges @_only_local
|
||||
@core @core_badges @_only_local @_file_upload
|
||||
Feature: Award badges
|
||||
In order to award badges to users for their achievements
|
||||
As an admin
|
||||
|
||||
@@ -73,11 +73,6 @@ if ($type == BADGE_TYPE_SITE) {
|
||||
$PAGE->set_context(context_course::instance($course->id));
|
||||
$PAGE->set_pagelayout('course');
|
||||
$PAGE->set_heading($title);
|
||||
|
||||
// Fix course navigation.
|
||||
$PAGE->navbar->ignore_active();
|
||||
$PAGE->navbar->add($course->shortname, new moodle_url('/course/view.php', array('id' => $course->id)));
|
||||
$PAGE->navbar->add(get_string('coursebadges', 'badges'));
|
||||
}
|
||||
|
||||
$PAGE->set_title($title);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@block @block_activity_modules @_only_local
|
||||
@block @block_activity_modules
|
||||
Feature: Block activity modules
|
||||
In order to overview activity modules in a course
|
||||
As a manager
|
||||
@@ -14,7 +14,7 @@ Feature: Block activity modules
|
||||
And I click on "//a[@title=\"Show\"]" "xpath_element" in the "Assignment (2.2)" "table_row"
|
||||
|
||||
Scenario: Add activities block on the frontpage
|
||||
And the following "activities" exists:
|
||||
Given the following "activities" exists:
|
||||
| activity | name | intro | course | idnumber |
|
||||
| assign | Frontpage assignment name | Frontpage assignment description | Acceptance test site | assign0 |
|
||||
| assignment | Frontpage assignment22 name | Frontpage assignment22 description | Acceptance test site | assignment0 |
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
/**
|
||||
* Commenting system steps definitions.
|
||||
*
|
||||
* @package core_comment
|
||||
* @package block_comments
|
||||
* @category test
|
||||
* @copyright 2013 David Monllaó
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
@@ -33,7 +33,7 @@ use Behat\Mink\Exception\ElementNotFoundException as ElementNotFoundException,
|
||||
/**
|
||||
* Steps definitions to deal with the commenting system
|
||||
*
|
||||
* @package core_comment
|
||||
* @package block_comments
|
||||
* @category test
|
||||
* @copyright 2013 David Monllaó
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
.block.block_html .content {padding:0;}
|
||||
.block.block_html .content .no-overflow {padding:4px;}
|
||||
@@ -16,7 +16,9 @@
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Manage files in folder in private area - to be replaced by something better hopefully....
|
||||
* Manage files in folder in private area.
|
||||
*
|
||||
* This page is not used and now redirects to the page to manage the private files.
|
||||
*
|
||||
* @package block_private_files
|
||||
* @copyright 2010 Petr Skoda (http://skodak.org)
|
||||
@@ -24,42 +26,5 @@
|
||||
*/
|
||||
|
||||
require('../../config.php');
|
||||
require_once("$CFG->dirroot/blocks/private_files/edit_form.php");
|
||||
require_once("$CFG->dirroot/repository/lib.php");
|
||||
|
||||
require_login();
|
||||
if (isguestuser()) {
|
||||
die();
|
||||
}
|
||||
//TODO: add capability check here!
|
||||
|
||||
$context = context_user::instance($USER->id);
|
||||
$title = get_string('privatefiles', 'block_private_files');
|
||||
$struser = get_string('user');
|
||||
|
||||
$PAGE->set_url('/blocks/private_files/edit.php');
|
||||
$PAGE->set_context($context);
|
||||
$PAGE->set_title($title);
|
||||
$PAGE->set_heading($title);
|
||||
$PAGE->set_pagelayout('mydashboard');
|
||||
$PAGE->set_pagetype('user-private-files');
|
||||
|
||||
$data = new stdClass();
|
||||
$options = array('subdirs'=>1, 'maxbytes'=>$CFG->userquota, 'maxfiles'=>-1, 'accepted_types'=>'*');
|
||||
file_prepare_standard_filemanager($data, 'files', $options, $context, 'user', 'private', 0);
|
||||
|
||||
$mform = new block_private_files_form(null, array('data'=>$data, 'options'=>$options));
|
||||
|
||||
if ($mform->is_cancelled()) {
|
||||
redirect(new moodle_url('/my/'));
|
||||
|
||||
} else if ($formdata = $mform->get_data()) {
|
||||
$formdata = file_postupdate_standard_filemanager($formdata, 'files', $options, $context, 'user', 'private', 0);
|
||||
redirect(new moodle_url('/my/'));
|
||||
}
|
||||
|
||||
echo $OUTPUT->header();
|
||||
echo $OUTPUT->box_start('generalbox');
|
||||
$mform->display();
|
||||
echo $OUTPUT->box_end();
|
||||
echo $OUTPUT->footer();
|
||||
redirect(new moodle_url('/user/files.php'));
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
/* Rule so that the table tree view works with word-wrap: break-word. */
|
||||
.block_private_files .content table {
|
||||
table-layout: fixed;
|
||||
width: 100%;
|
||||
}
|
||||
@@ -72,6 +72,8 @@ class block_site_main_menu extends block_list {
|
||||
$strmovefull = strip_tags(get_string('movefull', '', "'$USER->activitycopyname'"));
|
||||
$strcancel= get_string('cancel');
|
||||
$stractivityclipboard = $USER->activitycopyname;
|
||||
} else {
|
||||
$strmove = get_string('move');
|
||||
}
|
||||
$editbuttons = '';
|
||||
|
||||
@@ -90,18 +92,12 @@ class block_site_main_menu extends block_list {
|
||||
if (!$ismoving) {
|
||||
$actions = course_get_cm_edit_actions($mod, -1);
|
||||
|
||||
// Add the action move.
|
||||
$modcontext = context_module::instance($mod->id);
|
||||
$hasmanageactivities = has_capability('moodle/course:manageactivities', $modcontext);
|
||||
if ($hasmanageactivities) {
|
||||
$baseurl = new moodle_url('/course/mod.php', array('sesskey' => sesskey()));
|
||||
$actions['move'] = new action_menu_link_primary(
|
||||
new moodle_url($baseurl, array('copy' => $mod->id)),
|
||||
new pix_icon('t/move', get_string('move'), 'moodle', array('class' => 'iconsmall', 'title' => '')),
|
||||
null,
|
||||
array('title' => get_string('move'))
|
||||
);
|
||||
}
|
||||
// Prepend list of actions with the 'move' action.
|
||||
$actions = array('move' => new action_menu_link_primary(
|
||||
new moodle_url('/course/mod.php', array('sesskey' => sesskey(), 'copy' => $mod->id)),
|
||||
new pix_icon('t/move', $strmove, 'moodle', array('class' => 'iconsmall', 'title' => '')),
|
||||
$strmove
|
||||
)) + $actions;
|
||||
|
||||
$editbuttons = html_writer::tag('div',
|
||||
$courserenderer->course_section_cm_edit_actions($actions, $mod, array('donotenhance' => true)),
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
.block_site_main_menu li { clear: both; }
|
||||
.block_site_main_menu li .column { width: 100%; }
|
||||
.block_site_main_menu li .buttons { float: right; margin-top: 3px;}
|
||||
.block_site_main_menu li .buttons { float: right; margin: 0; }
|
||||
.dir-rtl .block_site_main_menu li .buttons { float: left; }
|
||||
.block_site_main_menu li .buttons a img{ vertical-align: text-bottom; margin: 0 3px;}
|
||||
.block_site_main_menu li .buttons a img{ vertical-align: text-bottom;}
|
||||
.block_site_main_menu .footer { margin-top: 1em; }
|
||||
.block_site_main_menu .section_add_menus noscript div { display: inline;}
|
||||
|
||||
@@ -74,6 +74,8 @@ class block_social_activities extends block_list {
|
||||
$strmovefull = strip_tags(get_string('movefull', '', "'$USER->activitycopyname'"));
|
||||
$strcancel= get_string('cancel');
|
||||
$stractivityclipboard = $USER->activitycopyname;
|
||||
} else {
|
||||
$strmove = get_string('move');
|
||||
}
|
||||
$editbuttons = '';
|
||||
|
||||
@@ -91,8 +93,18 @@ class block_social_activities extends block_list {
|
||||
}
|
||||
if (!$ismoving) {
|
||||
$actions = course_get_cm_edit_actions($mod, -1);
|
||||
$editbuttons = '<br />'.
|
||||
$courserenderer->course_section_cm_edit_actions($actions, $mod);
|
||||
|
||||
// Prepend list of actions with the 'move' action.
|
||||
$actions = array('move' => new action_menu_link_primary(
|
||||
new moodle_url('/course/mod.php', array('sesskey' => sesskey(), 'copy' => $mod->id)),
|
||||
new pix_icon('t/move', $strmove, 'moodle', array('class' => 'iconsmall', 'title' => '')),
|
||||
$strmove
|
||||
)) + $actions;
|
||||
|
||||
$editbuttons = html_writer::tag('div',
|
||||
$courserenderer->course_section_cm_edit_actions($actions, $mod, array('donotenhance' => true)),
|
||||
array('class' => 'buttons')
|
||||
);
|
||||
} else {
|
||||
$editbuttons = '';
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
.block_social_activities li { clear: both; }
|
||||
.block_social_activities li .column { width: 100%; }
|
||||
.block_social_activities li .buttons { float: right; margin: 0; }
|
||||
.dir-rtl .block_social_activities li .buttons { float: left; }
|
||||
.block_social_activities li .buttons a img{ vertical-align: text-bottom;}
|
||||
@@ -17,7 +17,7 @@
|
||||
/**
|
||||
* Steps definitions related with blocks.
|
||||
*
|
||||
* @package core
|
||||
* @package core_block
|
||||
* @category test
|
||||
* @copyright 2012 David Monllaó
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
@@ -32,7 +32,7 @@ use Behat\Behat\Context\Step\Given as Given;
|
||||
/**
|
||||
* Blocks management steps definitions.
|
||||
*
|
||||
* @package core
|
||||
* @package core_block
|
||||
* @category test
|
||||
* @copyright 2012 David Monllaó
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
|
||||
+3
-3
@@ -267,7 +267,7 @@ class blog_entry implements renderable {
|
||||
'objectid' => $this->id,
|
||||
'relateduserid' => $this->userid
|
||||
));
|
||||
$event->set_custom_data($this);
|
||||
$event->set_blog_entry($this);
|
||||
$event->trigger();
|
||||
}
|
||||
|
||||
@@ -309,7 +309,7 @@ class blog_entry implements renderable {
|
||||
'objectid' => $entry->id,
|
||||
'relateduserid' => $entry->userid
|
||||
));
|
||||
$event->set_custom_data($entry);
|
||||
$event->set_blog_entry($entry);
|
||||
$event->trigger();
|
||||
}
|
||||
|
||||
@@ -334,7 +334,7 @@ class blog_entry implements renderable {
|
||||
'relateduserid' => $this->userid
|
||||
));
|
||||
$event->add_record_snapshot("post", $record);
|
||||
$event->set_custom_data($this);
|
||||
$event->set_blog_entry($this);
|
||||
$event->trigger();
|
||||
}
|
||||
|
||||
|
||||
Vendored
+1
-1
@@ -1742,7 +1742,7 @@ class cache_session extends cache {
|
||||
* Purges the session cache of all data belonging to the current user.
|
||||
*/
|
||||
public function purge_current_user() {
|
||||
$keys = $this->get_store()->find_all($this->get_key_prefix());
|
||||
$keys = $this->get_store()->find_by_prefix($this->get_key_prefix());
|
||||
$this->get_store()->delete_many($keys);
|
||||
}
|
||||
|
||||
|
||||
Vendored
+1
-1
@@ -216,7 +216,7 @@ class cachestore_memcache extends cache_store implements cache_is_configurable {
|
||||
* @return int
|
||||
*/
|
||||
public static function get_supported_modes(array $configuration = array()) {
|
||||
return self::MODE_APPLICATION + self::MODE_SESSION;
|
||||
return self::MODE_APPLICATION;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Vendored
+1
-1
@@ -224,7 +224,7 @@ class cachestore_memcached extends cache_store implements cache_is_configurable
|
||||
* @return int
|
||||
*/
|
||||
public static function get_supported_modes(array $configuration = array()) {
|
||||
return self::MODE_APPLICATION + self::MODE_SESSION;
|
||||
return self::MODE_APPLICATION;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Vendored
+1
-1
@@ -175,7 +175,7 @@ class cachestore_mongodb extends cache_store implements cache_is_configurable {
|
||||
* @return int
|
||||
*/
|
||||
public static function get_supported_modes(array $configuration = array()) {
|
||||
return self::MODE_APPLICATION + self::MODE_SESSION;
|
||||
return self::MODE_APPLICATION;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -715,7 +715,8 @@ class core_calendar_renderer extends plugin_renderer_base {
|
||||
/**
|
||||
* Displays a course filter selector
|
||||
*
|
||||
* @param array $getvars
|
||||
* @param moodle_url $returnurl The URL that the user should be taken too upon selecting a course.
|
||||
* @param string $label The label to use for the course select.
|
||||
* @return string
|
||||
*/
|
||||
protected function course_filter_selector(moodle_url $returnurl, $label=null) {
|
||||
@@ -745,7 +746,9 @@ class core_calendar_renderer extends plugin_renderer_base {
|
||||
} else {
|
||||
$selected = '';
|
||||
}
|
||||
$select = new single_select(new moodle_url(CALENDAR_URL.'set.php', array('return' => base64_encode($returnurl->out(false)), 'var' => 'setcourse', 'sesskey'=>sesskey())), 'id', $courseoptions, $selected, null);
|
||||
$courseurl = new moodle_url($returnurl);
|
||||
$courseurl->remove_params('course');
|
||||
$select = new single_select($courseurl, 'course', $courseoptions, $selected, null);
|
||||
$select->class = 'cal_courses_flt';
|
||||
if ($label !== null) {
|
||||
$select->set_label($label);
|
||||
@@ -855,4 +858,4 @@ class core_calendar_renderer extends plugin_renderer_base {
|
||||
$html .= html_writer::end_tag('form');
|
||||
return $html;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -126,7 +126,7 @@ class cohort_existing_selector extends user_selector_base {
|
||||
|
||||
if (!$this->is_validating()) {
|
||||
$potentialmemberscount = $DB->count_records_sql($countfields . $sql, $params);
|
||||
if ($potentialmemberscount > 100) {
|
||||
if ($potentialmemberscount > $this->maxusersperpage) {
|
||||
return $this->too_many_results($search, $potentialmemberscount);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
/**
|
||||
* Cohorts steps definitions.
|
||||
*
|
||||
* @package core
|
||||
* @package core_cohort
|
||||
* @category test
|
||||
* @copyright 2013 David Monllaó
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
@@ -32,7 +32,7 @@ use Behat\Behat\Context\Step\Given as Given;
|
||||
/**
|
||||
* Steps definitions for cohort actions.
|
||||
*
|
||||
* @package core
|
||||
* @package core_cohort
|
||||
* @category test
|
||||
* @copyright 2013 David Monllaó
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@core @core_cohort @_only_local
|
||||
@core @core_cohort @_only_local @_file_upload
|
||||
Feature: Upload users to a cohort
|
||||
In order to quickly fill site-wide groups with users
|
||||
As an admin
|
||||
|
||||
@@ -61,7 +61,8 @@ class comment_manager {
|
||||
}
|
||||
$comments = array();
|
||||
|
||||
$sql = "SELECT c.id, c.contextid, c.itemid, c.commentarea, c.userid, c.content, u.firstname, u.lastname, c.timecreated
|
||||
$usernamefields = get_all_user_name_fields(true, 'u');
|
||||
$sql = "SELECT c.id, c.contextid, c.itemid, c.commentarea, c.userid, c.content, $usernamefields, c.timecreated
|
||||
FROM {comments} c
|
||||
JOIN {user} u
|
||||
ON u.id=c.userid
|
||||
@@ -74,8 +75,9 @@ class comment_manager {
|
||||
$item->time = userdate($item->timecreated);
|
||||
$item->content = format_text($item->content, FORMAT_MOODLE, $formatoptions);
|
||||
// Unset fields not related to the comment
|
||||
unset($item->firstname);
|
||||
unset($item->lastname);
|
||||
foreach (get_all_user_name_fields() as $namefield) {
|
||||
unset($item->$namefield);
|
||||
}
|
||||
unset($item->timecreated);
|
||||
// Record the comment
|
||||
$comments[] = $item;
|
||||
|
||||
@@ -165,7 +165,7 @@ class completion_criteria_duration extends completion_criteria {
|
||||
* @return string
|
||||
*/
|
||||
public function get_title_detailed() {
|
||||
return ceil($this->enrolperiod / (60 * 60 * 24)) . ' days';
|
||||
return get_string('xdays', 'completion', ceil($this->enrolperiod / (60 * 60 * 24)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -190,7 +190,8 @@ class completion_criteria_duration extends completion_criteria {
|
||||
|
||||
$daysleft = ceil($timeleft / (60 * 60 * 24));
|
||||
|
||||
return ($daysleft > 0 ? $daysleft : 0).' of '.$enrolperiod;
|
||||
return get_string('daysoftotal', 'completion', array(
|
||||
'days' => $daysleft > 0 ? $daysleft : 0, 'total' => $enrolperiod));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -37,13 +37,13 @@ Feature: Restrict activity availability through date conditions
|
||||
| assignsubmission_file_enabled | 0 |
|
||||
| id_availablefrom_day | 31 |
|
||||
| id_availablefrom_month | 12 |
|
||||
| id_availablefrom_year | 2050 |
|
||||
| id_availablefrom_year | 2037 |
|
||||
| id_showavailability | 1 |
|
||||
And I press "Save and return to course"
|
||||
And I log out
|
||||
When I log in as "student1"
|
||||
And I follow "Course 1"
|
||||
Then I should see "Available from 31 December 2050."
|
||||
Then I should see "Available from 31 December 2037."
|
||||
And "Test assignment 1" activity should be hidden
|
||||
And I log out
|
||||
|
||||
|
||||
+1
-1
@@ -8,6 +8,6 @@
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "3.7.*",
|
||||
"phpunit/dbUnit": "1.2.*",
|
||||
"moodlehq/behat-extension": "1.26.4"
|
||||
"moodlehq/behat-extension": "1.26.7"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -667,6 +667,12 @@ $CFG->admin = 'admin';
|
||||
// Example:
|
||||
// $CFG->behat_extraallowedsettings = array('logsql', 'dblogerror');
|
||||
//
|
||||
// You can make behat save several dumps when a scenario fails. The dumps currently saved are:
|
||||
// * a dump of the DOM in it's state at the time of failure; and
|
||||
// * a screenshot (JavaScript is required for the screenshot functionality, so not all browsers support this option)
|
||||
// Example:
|
||||
// $CFG->behat_faildump_path = '/my/path/to/save/failure/dumps';
|
||||
//
|
||||
//=========================================================================
|
||||
// 12. DEVELOPER DATA GENERATOR
|
||||
//=========================================================================
|
||||
|
||||
+26
-21
@@ -102,31 +102,32 @@ M.course_dndupload = {
|
||||
* is available (or to explain why it is not available)
|
||||
*/
|
||||
add_status_div: function() {
|
||||
var coursecontents = document.getElementById(this.pagecontentid);
|
||||
var Y = this.Y,
|
||||
coursecontents = Y.one('#' + this.pagecontentid),
|
||||
div,
|
||||
handlefile = (this.handlers.filehandlers.length > 0),
|
||||
handletext = false,
|
||||
handlelink = false,
|
||||
i = 0,
|
||||
styletop,
|
||||
styletopunit;
|
||||
|
||||
if (!coursecontents) {
|
||||
return;
|
||||
}
|
||||
|
||||
var div = document.createElement('div');
|
||||
div.id = 'dndupload-status';
|
||||
div.style.opacity = 0.0;
|
||||
coursecontents.insertBefore(div, coursecontents.firstChild);
|
||||
div = Y.Node.create('<div id="dndupload-status"></div>').setStyle('opacity', '0.0');
|
||||
coursecontents.insert(div, 0);
|
||||
|
||||
var Y = this.Y;
|
||||
div = Y.one(div);
|
||||
var handlefile = (this.handlers.filehandlers.length > 0);
|
||||
var handletext = false;
|
||||
var handlelink = false;
|
||||
var i;
|
||||
for (i=0; i<this.handlers.types.length; i++) {
|
||||
for (i = 0; i < this.handlers.types.length; i++) {
|
||||
switch (this.handlers.types[i].identifier) {
|
||||
case 'text':
|
||||
case 'text/html':
|
||||
handletext = true;
|
||||
break;
|
||||
case 'url':
|
||||
handlelink = true;
|
||||
break;
|
||||
case 'text':
|
||||
case 'text/html':
|
||||
handletext = true;
|
||||
break;
|
||||
case 'url':
|
||||
handlelink = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
$msgident = 'dndworking';
|
||||
@@ -141,16 +142,20 @@ M.course_dndupload = {
|
||||
}
|
||||
div.setContent(M.util.get_string($msgident, 'moodle'));
|
||||
|
||||
styletop = div.getStyle('top') || '0px';
|
||||
styletopunit = styletop.replace(/^\d+/, '');
|
||||
styletop = parseInt(styletop.replace(/\D*$/, ''), 10);
|
||||
|
||||
var fadeanim = new Y.Anim({
|
||||
node: '#dndupload-status',
|
||||
from: {
|
||||
opacity: 0.0,
|
||||
top: '-30px'
|
||||
top: (styletop - 30).toString() + styletopunit
|
||||
},
|
||||
|
||||
to: {
|
||||
opacity: 1.0,
|
||||
top: '0px'
|
||||
top: styletop.toString() + styletopunit
|
||||
},
|
||||
duration: 0.5
|
||||
});
|
||||
|
||||
+4
-1
@@ -45,8 +45,11 @@ if ($id) {
|
||||
print_error('cannoteditsiteform');
|
||||
}
|
||||
|
||||
$course = course_get_format($id)->get_course();
|
||||
// Login to the course and retrieve also all fields defined by course format.
|
||||
$course = get_course($id);
|
||||
require_login($course);
|
||||
$course = course_get_format($course)->get_course();
|
||||
|
||||
$category = $DB->get_record('course_categories', array('id'=>$course->category), '*', MUST_EXIST);
|
||||
$coursecontext = context_course::instance($course->id);
|
||||
require_capability('moodle/course:update', $coursecontext);
|
||||
|
||||
@@ -321,6 +321,7 @@ class course_edit_form extends moodleform {
|
||||
$options[$grouping->id] = format_string($grouping->name);
|
||||
}
|
||||
}
|
||||
core_collator::asort($options);
|
||||
$gr_el =& $mform->getElement('defaultgroupingid');
|
||||
$gr_el->load($options);
|
||||
}
|
||||
|
||||
@@ -67,13 +67,14 @@ class editsection_form extends moodleform {
|
||||
// Grouping conditions - only if grouping is enabled at site level
|
||||
if (!empty($CFG->enablegroupmembersonly)) {
|
||||
$options = array();
|
||||
$options[0] = get_string('none');
|
||||
if ($groupings = $DB->get_records('groupings', array('courseid' => $course->id))) {
|
||||
foreach ($groupings as $grouping) {
|
||||
$options[$grouping->id] = format_string(
|
||||
$grouping->name, true, array('context' => $context));
|
||||
}
|
||||
}
|
||||
core_collator::asort($options);
|
||||
$options = array(0 => get_string('none')) + $options;
|
||||
$mform->addElement('select', 'groupingid', get_string('groupingsection', 'group'), $options);
|
||||
$mform->addHelpButton('groupingid', 'groupingsection', 'group');
|
||||
}
|
||||
|
||||
+13
-6
@@ -237,14 +237,21 @@ abstract class format_base {
|
||||
if ($this->course === false) {
|
||||
$this->course = get_course($this->courseid);
|
||||
$options = $this->get_format_options();
|
||||
$dbcoursecolumns = null;
|
||||
foreach ($options as $optionname => $optionvalue) {
|
||||
if (!isset($this->course->$optionname)) {
|
||||
$this->course->$optionname = $optionvalue;
|
||||
} else {
|
||||
debugging('The option name '.$optionname.' in course format '.$this->format.
|
||||
' is invalid because the field with the same name exists in {course} table',
|
||||
DEBUG_DEVELOPER);
|
||||
if (isset($this->course->$optionname)) {
|
||||
// Course format options must not have the same names as existing columns in db table "course".
|
||||
if (!isset($dbcoursecolumns)) {
|
||||
$dbcoursecolumns = $DB->get_columns('course');
|
||||
}
|
||||
if (isset($dbcoursecolumns[$optionname])) {
|
||||
debugging('The option name '.$optionname.' in course format '.$this->format.
|
||||
' is invalid because the field with the same name exists in {course} table',
|
||||
DEBUG_DEVELOPER);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
$this->course->$optionname = $optionvalue;
|
||||
}
|
||||
}
|
||||
return $this->course;
|
||||
|
||||
@@ -373,7 +373,8 @@ abstract class format_section_renderer_base extends plugin_renderer_base {
|
||||
if ($cancomplete && $completioninfo->is_enabled($thismod) != COMPLETION_TRACKING_NONE) {
|
||||
$total++;
|
||||
$completiondata = $completioninfo->get_data($thismod, true);
|
||||
if ($completiondata->completionstate == COMPLETION_COMPLETE) {
|
||||
if ($completiondata->completionstate == COMPLETION_COMPLETE ||
|
||||
$completiondata->completionstate == COMPLETION_COMPLETE_PASS) {
|
||||
$complete++;
|
||||
}
|
||||
}
|
||||
|
||||
+10
-4
@@ -70,7 +70,6 @@ function make_log_url($module, $url) {
|
||||
case 'login':
|
||||
case 'lib':
|
||||
case 'admin':
|
||||
case 'calendar':
|
||||
case 'category':
|
||||
case 'mnet course':
|
||||
if (strpos($url, '../') === 0) {
|
||||
@@ -79,6 +78,9 @@ function make_log_url($module, $url) {
|
||||
$url = "/course/$url";
|
||||
}
|
||||
break;
|
||||
case 'calendar':
|
||||
$url = "/calendar/$url";
|
||||
break;
|
||||
case 'user':
|
||||
case 'blog':
|
||||
$url = "/$module/$url";
|
||||
@@ -1982,7 +1984,7 @@ function course_get_cm_edit_actions(cm_info $mod, $indent = -1, $sr = null) {
|
||||
}
|
||||
|
||||
// Indent.
|
||||
if ($hasmanageactivities) {
|
||||
if ($hasmanageactivities && $indent >= 0) {
|
||||
$indentlimits = new stdClass();
|
||||
$indentlimits->min = 0;
|
||||
$indentlimits->max = 16;
|
||||
@@ -3241,11 +3243,15 @@ function include_course_ajax($course, $usedmodules = array(), $enabledmodules =
|
||||
'clicktochangeinbrackets',
|
||||
'markthistopic',
|
||||
'markedthistopic',
|
||||
'move',
|
||||
'movesection',
|
||||
'movecoursemodule',
|
||||
'movecoursesection',
|
||||
'movecontent',
|
||||
'tocontent',
|
||||
'emptydragdropregion'
|
||||
'emptydragdropregion',
|
||||
'afterresource',
|
||||
'aftersection',
|
||||
'totopofsection',
|
||||
), 'moodle');
|
||||
|
||||
// Include section-specific strings for formats which support sections.
|
||||
|
||||
+14
-3
@@ -85,6 +85,9 @@ function add_moduleinfo($moduleinfo, $course, $mform = null) {
|
||||
$newcm->showdescription = 0;
|
||||
}
|
||||
|
||||
// From this point we make database changes, so start transaction.
|
||||
$transaction = $DB->start_delegated_transaction();
|
||||
|
||||
if (!$moduleinfo->coursemodule = add_course_module($newcm)) {
|
||||
print_error('cannotaddcoursemodule');
|
||||
}
|
||||
@@ -98,14 +101,21 @@ function add_moduleinfo($moduleinfo, $course, $mform = null) {
|
||||
}
|
||||
|
||||
$addinstancefunction = $moduleinfo->modulename."_add_instance";
|
||||
$returnfromfunc = $addinstancefunction($moduleinfo, $mform);
|
||||
try {
|
||||
$returnfromfunc = $addinstancefunction($moduleinfo, $mform);
|
||||
} catch (moodle_exception $e) {
|
||||
$returnfromfunc = $e;
|
||||
}
|
||||
if (!$returnfromfunc or !is_number($returnfromfunc)) {
|
||||
// Undo everything we can.
|
||||
// Undo everything we can. This is not necessary for databases which
|
||||
// support transactions, but improves consistency for other databases.
|
||||
$modcontext = context_module::instance($moduleinfo->coursemodule);
|
||||
context_helper::delete_instance(CONTEXT_MODULE, $moduleinfo->coursemodule);
|
||||
$DB->delete_records('course_modules', array('id'=>$moduleinfo->coursemodule));
|
||||
|
||||
if (!is_number($returnfromfunc)) {
|
||||
if ($e instanceof moodle_exception) {
|
||||
throw $e;
|
||||
} else if (!is_number($returnfromfunc)) {
|
||||
print_error('invalidfunction', '', course_get_url($course, $moduleinfo->section));
|
||||
} else {
|
||||
print_error('cannotaddnewmodule', '', course_get_url($course, $moduleinfo->section), $moduleinfo->modulename);
|
||||
@@ -152,6 +162,7 @@ function add_moduleinfo($moduleinfo, $course, $mform = null) {
|
||||
"$moduleinfo->instance", $moduleinfo->coursemodule);
|
||||
|
||||
$moduleinfo = edit_module_post_actions($moduleinfo, $course);
|
||||
$transaction->allow_commit();
|
||||
|
||||
return $moduleinfo;
|
||||
}
|
||||
|
||||
@@ -504,12 +504,13 @@ abstract class moodleform_mod extends moodleform {
|
||||
if ($this->_features->groupings or $this->_features->groupmembersonly) {
|
||||
//groupings selector - used for normal grouping mode or also when restricting access with groupmembersonly
|
||||
$options = array();
|
||||
$options[0] = get_string('none');
|
||||
if ($groupings = $DB->get_records('groupings', array('courseid'=>$COURSE->id))) {
|
||||
foreach ($groupings as $grouping) {
|
||||
$options[$grouping->id] = format_string($grouping->name);
|
||||
}
|
||||
}
|
||||
core_collator::asort($options);
|
||||
$options = array(0 => get_string('none')) + $options;
|
||||
$mform->addElement('select', 'groupingid', get_string('grouping', 'group'), $options);
|
||||
$mform->addHelpButton('groupingid', 'grouping', 'group');
|
||||
}
|
||||
|
||||
+5
-3
@@ -1506,7 +1506,7 @@ class core_course_renderer extends plugin_renderer_base {
|
||||
protected function coursecat_include_js() {
|
||||
global $CFG;
|
||||
static $jsloaded = false;
|
||||
if (!$jsloaded && $CFG->enableajax) {
|
||||
if (!$jsloaded) {
|
||||
// We must only load this module once.
|
||||
$this->page->requires->yui_module('moodle-course-categoryexpander',
|
||||
'Y.Moodle.course.categoryexpander.init');
|
||||
@@ -1586,8 +1586,6 @@ class core_course_renderer extends plugin_renderer_base {
|
||||
$classes[] = 'with_children';
|
||||
$classes[] = 'collapsed';
|
||||
}
|
||||
// Make sure JS file to expand category content is included.
|
||||
$this->coursecat_include_js();
|
||||
} else {
|
||||
// load category content
|
||||
$categorycontent = $this->coursecat_category_content($chelper, $coursecat, $depth);
|
||||
@@ -1596,6 +1594,10 @@ class core_course_renderer extends plugin_renderer_base {
|
||||
$classes[] = 'with_children';
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure JS file to expand category content is included.
|
||||
$this->coursecat_include_js();
|
||||
|
||||
$content = html_writer::start_tag('div', array(
|
||||
'class' => join(' ', $classes),
|
||||
'data-categoryid' => $coursecat->id,
|
||||
|
||||
@@ -39,3 +39,57 @@ Feature: Toggle activities visibility from the course page
|
||||
And I log in as "student1"
|
||||
And I follow "Course 1"
|
||||
And "Test forum name" activity should be hidden
|
||||
|
||||
@javascript
|
||||
Scenario: Activities can be shown and hidden inside a hidden section
|
||||
Given the following "users" exists:
|
||||
| username | firstname | lastname | email |
|
||||
| teacher1 | Teacher | 1 | teacher1@asd.com |
|
||||
And the following "courses" exists:
|
||||
| fullname | shortname | format | numsections |
|
||||
| Course 1 | C1 | topics | 2 |
|
||||
And the following "course enrolments" exists:
|
||||
| user | course | role |
|
||||
| teacher1 | C1 | editingteacher |
|
||||
And I log in as "teacher1"
|
||||
And I follow "Course 1"
|
||||
And I turn editing mode on
|
||||
And I add a "Forum" to section "2" and I fill the form with:
|
||||
| Forum name | Test forum name |
|
||||
| Description | Test forum description |
|
||||
| Visible | Show |
|
||||
When I hide section "2"
|
||||
Then "Test forum name" activity should be hidden
|
||||
And I open "Test forum name" actions menu
|
||||
And I click on "Show" "link" in the "Test forum name" activity
|
||||
And "Test forum name" activity should be visible
|
||||
And I open "Test forum name" actions menu
|
||||
And I click on "Hide" "link" in the "Test forum name" activity
|
||||
And "Test forum name" activity should be hidden
|
||||
|
||||
@javascript
|
||||
Scenario: Activities can be shown and hidden inside an orphaned section
|
||||
Given the following "users" exists:
|
||||
| username | firstname | lastname | email |
|
||||
| teacher1 | Teacher | 1 | teacher1@asd.com |
|
||||
And the following "courses" exists:
|
||||
| fullname | shortname | format | numsections |
|
||||
| Course 1 | C1 | topics | 2 |
|
||||
And the following "course enrolments" exists:
|
||||
| user | course | role |
|
||||
| teacher1 | C1 | editingteacher |
|
||||
And I log in as "teacher1"
|
||||
And I follow "Course 1"
|
||||
And I turn editing mode on
|
||||
And I add a "Forum" to section "2" and I fill the form with:
|
||||
| Forum name | Test forum name |
|
||||
| Description | Test forum description |
|
||||
| Visible | Show |
|
||||
When I click on ".reduce-sections" "css_element"
|
||||
Then "Test forum name" activity should be visible
|
||||
And I open "Test forum name" actions menu
|
||||
And I click on "Hide" "link" in the "Test forum name" activity
|
||||
And "Test forum name" activity should be hidden
|
||||
And I open "Test forum name" actions menu
|
||||
And I click on "Show" "link" in the "Test forum name" activity
|
||||
And "Test forum name" activity should be visible
|
||||
|
||||
@@ -351,6 +351,23 @@ class behat_course extends behat_base {
|
||||
// Section should be hidden.
|
||||
$exception = new ExpectationException('The section is not hidden', $this->getSession());
|
||||
$this->find('xpath', $sectionxpath . "[contains(concat(' ', normalize-space(@class), ' '), ' hidden ')]", $exception);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that all actiities in the specified section are hidden. You need to be in the course page. It can be used being logged as a student and as a teacher on editing mode.
|
||||
*
|
||||
* @Then /^all activities in section "(?P<section_number>\d+)" should be hidden$/
|
||||
* @throws ExpectationException
|
||||
* @throws ElementNotFoundException Thrown by behat_base::find
|
||||
* @param int $sectionnumber
|
||||
*/
|
||||
public function section_activities_should_be_hidden($sectionnumber) {
|
||||
$sectionxpath = $this->section_exists($sectionnumber);
|
||||
|
||||
// Preventive in case there is any action in progress.
|
||||
// Adding it here because we are interacting (click) with
|
||||
// the elements, not necessary when we just find().
|
||||
$this->i_wait_until_section_is_available($sectionnumber);
|
||||
|
||||
// The checking are different depending on user permissions.
|
||||
if ($this->is_course_editor()) {
|
||||
@@ -362,50 +379,19 @@ class behat_course extends behat_base {
|
||||
if ($activities = $this->get_section_activities($sectionxpath)) {
|
||||
|
||||
$dimmedexception = new ExpectationException('There are activities that are not dimmed', $this->getSession());
|
||||
$visibilityexception = new ExpectationException('There are activities which visibility icons are clickable', $this->getSession());
|
||||
foreach ($activities as $activity) {
|
||||
|
||||
// Dimmed.
|
||||
$this->find('xpath', "//div[contains(concat(' ', normalize-space(@class), ' '), ' activityinstance ')]" .
|
||||
"/a[contains(concat(' ', normalize-space(@class), ' '), ' dimmed ')]", $dimmedexception, $activity);
|
||||
|
||||
// Non-JS browsers can not click on img elements.
|
||||
if ($this->running_javascript()) {
|
||||
|
||||
// Expanding the actions menu if it is not shown.
|
||||
$classes = array_flip(explode(' ', $activity->getAttribute('class')));
|
||||
if (empty($classes['action-menu-shown'])) {
|
||||
$actionsmenu = $this->find('css', "a[role='menuitem']", false, $activity);
|
||||
$actionsmenu->click();
|
||||
}
|
||||
|
||||
// To check that the visibility is not clickable we check the funcionality rather than the applied style.
|
||||
$visibilityiconnode = $this->find('css', 'a.editing_show img', false, $activity);
|
||||
$visibilityiconnode->click();
|
||||
}
|
||||
|
||||
// We ensure that we still see the show icon.
|
||||
$visibilityiconnode = $this->find('css', 'a.editing_show img', $visibilityexception, $activity);
|
||||
|
||||
// It is there only when running JS scenarios.
|
||||
if ($this->running_javascript()) {
|
||||
|
||||
// Collapse the actions menu if it is displayed.
|
||||
$classes = array_flip(explode(' ', $activity->getAttribute('class')));
|
||||
if (!empty($classes['action-menu-shown'])) {
|
||||
$actionsmenu = $this->find('css', "a[role='menuitem']", false, $activity);
|
||||
$actionsmenu->click();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
// There shouldn't be activities.
|
||||
if ($this->get_section_activities($sectionxpath)) {
|
||||
throw new ExpectationException('There are activities in the section and they should be hidden', $this->getSession());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
@core @core_course @_alerts
|
||||
@core @core_course
|
||||
Feature: Course activity controls works as expected
|
||||
In order to manage my course's activities
|
||||
As a teacher
|
||||
I need to edit, hide, show and indent activities inside course sections
|
||||
|
||||
# The difference between these two scenario outlines is that one is with
|
||||
# JS enabled and the other one with JS disabled, also with JS disabled we
|
||||
# add the delete activity checking; we can not use Background sections
|
||||
# when using Scenario Outlines because of Behat framework restrictions.
|
||||
# JS enabled and the other one with JS disabled; we can not use Background
|
||||
# sections when using Scenario Outlines because of Behat framework restrictions.
|
||||
|
||||
# We are testing:
|
||||
# * Javascript on and off
|
||||
@@ -16,7 +15,7 @@ Feature: Course activity controls works as expected
|
||||
# * Course controls with paged mode in the course home page
|
||||
# * Course controls with paged mode in a section's page
|
||||
|
||||
@javascript @_cross_browser
|
||||
@javascript @_cross_browser @_alert
|
||||
Scenario Outline: General activities course controls using topics and weeks formats, and paged mode and not paged mode works as expected
|
||||
Given the following "users" exists:
|
||||
| username | firstname | lastname | email |
|
||||
@@ -68,7 +67,9 @@ Feature: Course activity controls works as expected
|
||||
And I open "Test forum name 1" actions menu
|
||||
And I click on "Hide" "link" in the "Test forum name 1" activity
|
||||
And "#section-2" "css_element" <should_see_other_sections> exists
|
||||
And I close "Test forum name 1" actions menu
|
||||
And I open "Test forum name 1" actions menu
|
||||
And I delete "Test forum name 1" activity
|
||||
And I should not see "Test forum name 1" in the "#region-main" "css_element"
|
||||
And I duplicate "Test forum name 2" activity editing the new copy with:
|
||||
| Forum name | Edited test forum name 2 |
|
||||
And "#section-2" "css_element" <should_see_other_sections> exists
|
||||
@@ -77,6 +78,7 @@ Feature: Course activity controls works as expected
|
||||
And I hide section "1"
|
||||
And "#section-2" "css_element" <should_see_other_sections> exists
|
||||
And section "1" should be hidden
|
||||
And all activities in section "1" should be hidden
|
||||
And I show section "1"
|
||||
And "#section-2" "css_element" <should_see_other_sections> exists
|
||||
And section "1" should be visible
|
||||
@@ -151,6 +153,7 @@ Feature: Course activity controls works as expected
|
||||
And I hide section "1"
|
||||
And "#section-2" "css_element" <should_see_other_sections> exists
|
||||
And section "1" should be hidden
|
||||
And all activities in section "1" should be hidden
|
||||
And I show section "1"
|
||||
And "#section-2" "css_element" <should_see_other_sections> exists
|
||||
And section "1" should be visible
|
||||
|
||||
@@ -59,11 +59,15 @@ Feature: Show/hide course sections
|
||||
And section "3" should be hidden
|
||||
And I reload the page
|
||||
And section "1" should be hidden
|
||||
And all activities in section "1" should be hidden
|
||||
And section "2" should be visible
|
||||
And section "3" should be hidden
|
||||
And all activities in section "1" should be hidden
|
||||
And I log out
|
||||
And I log in as "student1"
|
||||
And I follow "Course 1"
|
||||
And section "1" should be hidden
|
||||
And all activities in section "1" should be hidden
|
||||
And section "2" should be visible
|
||||
And section "3" should be hidden
|
||||
And all activities in section "1" should be hidden
|
||||
|
||||
@@ -1912,7 +1912,7 @@ class core_course_courselib_testcase extends advanced_testcase {
|
||||
$this->assertEquals($cm->id, $event->objectid);
|
||||
$this->assertEquals($USER->id, $event->userid);
|
||||
$this->assertEquals('course_modules', $event->objecttable);
|
||||
$url = new moodle_url('/mod/assign/view.php', array('id' => $mod->id));
|
||||
$url = new moodle_url('/mod/assign/view.php', array('id' => $cm->id));
|
||||
$this->assertEquals($url, $event->get_url());
|
||||
|
||||
// Test legacy data.
|
||||
@@ -1925,7 +1925,7 @@ class core_course_courselib_testcase extends advanced_testcase {
|
||||
$eventdata->userid = $USER->id;
|
||||
$this->assertEventLegacyData($eventdata, $event);
|
||||
|
||||
$arr = array($cm->course, "course", "add mod", "../mod/assign/view.php?id=$mod->id", "assign $cm->instance");
|
||||
$arr = array($cm->course, "course", "add mod", "../mod/assign/view.php?id=$cm->id", "assign $cm->instance");
|
||||
$this->assertEventLegacyLogData($arr, $event);
|
||||
|
||||
}
|
||||
@@ -2017,7 +2017,7 @@ class core_course_courselib_testcase extends advanced_testcase {
|
||||
$this->assertEquals($cm->id, $event->objectid);
|
||||
$this->assertEquals($USER->id, $event->userid);
|
||||
$this->assertEquals('course_modules', $event->objecttable);
|
||||
$url = new moodle_url('/mod/forum/view.php', array('id' => $mod->id));
|
||||
$url = new moodle_url('/mod/forum/view.php', array('id' => $cm->id));
|
||||
$this->assertEquals($url, $event->get_url());
|
||||
|
||||
// Test legacy data.
|
||||
@@ -2030,7 +2030,7 @@ class core_course_courselib_testcase extends advanced_testcase {
|
||||
$eventdata->userid = $USER->id;
|
||||
$this->assertEventLegacyData($eventdata, $event);
|
||||
|
||||
$arr = array($cm->course, "course", "update mod", "../mod/forum/view.php?id=$mod->id", "forum $cm->instance");
|
||||
$arr = array($cm->course, "course", "update mod", "../mod/forum/view.php?id=$cm->id", "forum $cm->instance");
|
||||
$this->assertEventLegacyLogData($arr, $event);
|
||||
|
||||
}
|
||||
|
||||
Vendored
+10
-1
@@ -278,7 +278,16 @@ YUI.add('moodle-course-dragdrop', function(Y) {
|
||||
this.groups = ['resource'];
|
||||
this.samenodeclass = CSS.ACTIVITY;
|
||||
this.parentnodeclass = CSS.SECTION;
|
||||
this.resourcedraghandle = this.get_drag_handle(M.str.moodle.move, CSS.EDITINGMOVE, CSS.ICONCLASS, true);
|
||||
this.resourcedraghandle = this.get_drag_handle(M.util.get_string('movecoursemodule', 'moodle'), CSS.EDITINGMOVE, CSS.ICONCLASS, true);
|
||||
|
||||
this.samenodelabel = {
|
||||
identifier: 'afterresource',
|
||||
component: 'moodle'
|
||||
};
|
||||
this.parentnodelabel = {
|
||||
identifier: 'totopofsection',
|
||||
component: 'moodle'
|
||||
};
|
||||
|
||||
// Go through all sections
|
||||
var sectionlistselector = M.course.format.get_section_selector(Y);
|
||||
|
||||
Vendored
-6
@@ -481,12 +481,6 @@ YUI.add('moodle-course-toolboxes', function(Y) {
|
||||
// Prevent the default button action
|
||||
ev.preventDefault();
|
||||
|
||||
// Return early if the current section is hidden
|
||||
var section = activity.ancestor(M.course.format.get_section_selector(Y));
|
||||
if (section && section.hasClass(CSS.SECTIONHIDDENCLASS)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the element we're working on
|
||||
var element = activity;
|
||||
var value = this.handle_resource_dim(button, activity, action);
|
||||
|
||||
@@ -113,6 +113,7 @@ class core_enrol_external extends external_api {
|
||||
foreach ($thefields as $f) {
|
||||
$userfields[] = clean_param($f, PARAM_ALPHANUMEXT);
|
||||
}
|
||||
break;
|
||||
case 'limitfrom' :
|
||||
$limitfrom = clean_param($option['value'], PARAM_INT);
|
||||
break;
|
||||
@@ -414,6 +415,7 @@ class core_enrol_external extends external_api {
|
||||
foreach ($thefields as $f) {
|
||||
$userfields[] = clean_param($f, PARAM_ALPHANUMEXT);
|
||||
}
|
||||
break;
|
||||
case 'limitfrom' :
|
||||
$limitfrom = clean_param($option['value'], PARAM_INT);
|
||||
break;
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
require_once(dirname(dirname(dirname(__FILE__))) . '/config.php');
|
||||
require_login(0, false);
|
||||
require_capability('moodle/site:config', context_system::instance());
|
||||
require_sesskey();
|
||||
|
||||
$site = get_site();
|
||||
|
||||
|
||||
@@ -119,7 +119,8 @@ if ($ADMIN->fulltree) {
|
||||
$settings->add(new admin_setting_configcheckbox('enrol_imsenterprise/imscapitafix',
|
||||
get_string('usecapitafix', 'enrol_imsenterprise'), get_string('usecapitafix_desc', 'enrol_imsenterprise'), 0));
|
||||
|
||||
$importnowstring = get_string('aftersaving...', 'enrol_imsenterprise').' <a href="../enrol/imsenterprise/importnow.php">';
|
||||
$importnowstring .= get_string('doitnow', 'enrol_imsenterprise').'</a>';
|
||||
$importurl = new moodle_url('/enrol/imsenterprise/importnow.php', array('sesskey' => sesskey()));
|
||||
$importnowstring = get_string('aftersaving...', 'enrol_imsenterprise').' ';
|
||||
$importnowstring .= html_writer::link($importurl, get_string('doitnow', 'enrol_imsenterprise'));
|
||||
$settings->add(new admin_setting_heading('enrol_imsenterprise_doitnowmessage', '', $importnowstring));
|
||||
}
|
||||
|
||||
@@ -109,6 +109,7 @@ class enrol_manual_external extends external_api {
|
||||
}
|
||||
|
||||
// Check manual enrolment plugin instance is enabled/exist.
|
||||
$instance = null;
|
||||
$enrolinstances = enrol_get_instances($enrolment['courseid'], true);
|
||||
foreach ($enrolinstances as $courseenrolinstance) {
|
||||
if ($courseenrolinstance->enrol == "manual") {
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
* Enrol manual external PHPunit tests
|
||||
*
|
||||
* @package enrol_manual
|
||||
* @category external
|
||||
* @category phpunit
|
||||
* @copyright 2012 Jerome Mouneyrac
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @since Moodle 2.4
|
||||
@@ -31,43 +31,87 @@ global $CFG;
|
||||
require_once($CFG->dirroot . '/webservice/tests/helpers.php');
|
||||
require_once($CFG->dirroot . '/enrol/manual/externallib.php');
|
||||
|
||||
class enrol_manual_external_testcase extends externallib_advanced_testcase {
|
||||
class enrol_manual_externallib_testcase extends externallib_advanced_testcase {
|
||||
|
||||
/**
|
||||
* Test get_enrolled_users
|
||||
*/
|
||||
public function test_enrol_users() {
|
||||
global $USER, $CFG;
|
||||
global $DB;
|
||||
|
||||
$this->resetAfterTest(true);
|
||||
|
||||
$course = self::getDataGenerator()->create_course();
|
||||
$user = self::getDataGenerator()->create_user();
|
||||
$this->setUser($user);
|
||||
|
||||
$course1 = self::getDataGenerator()->create_course();
|
||||
$course2 = self::getDataGenerator()->create_course();
|
||||
$user1 = self::getDataGenerator()->create_user();
|
||||
$user2 = self::getDataGenerator()->create_user();
|
||||
|
||||
// Set the required capabilities by the external function.
|
||||
$context = context_course::instance($course->id);
|
||||
$roleid = $this->assignUserCapability('enrol/manual:enrol', $context->id);
|
||||
$this->assignUserCapability('moodle/course:view', $context->id, $roleid);
|
||||
$context1 = context_course::instance($course1->id);
|
||||
$context2 = context_course::instance($course2->id);
|
||||
$instance1 = $DB->get_record('enrol', array('courseid' => $course1->id, 'enrol' => 'manual'), '*', MUST_EXIST);
|
||||
$instance2 = $DB->get_record('enrol', array('courseid' => $course2->id, 'enrol' => 'manual'), '*', MUST_EXIST);
|
||||
|
||||
// Add manager role to $USER.
|
||||
// So $USER is allowed to assign 'manager', 'editingteacher', 'teacher' and 'student'.
|
||||
role_assign(1, $USER->id, context_system::instance()->id);
|
||||
// Set the required capabilities by the external function.
|
||||
$roleid = $this->assignUserCapability('enrol/manual:enrol', $context1->id);
|
||||
$this->assignUserCapability('moodle/course:view', $context1->id, $roleid);
|
||||
$this->assignUserCapability('moodle/role:assign', $context1->id, $roleid);
|
||||
$this->assignUserCapability('enrol/manual:enrol', $context2->id, $roleid);
|
||||
$this->assignUserCapability('moodle/course:view', $context2->id, $roleid);
|
||||
$this->assignUserCapability('moodle/role:assign', $context2->id, $roleid);
|
||||
|
||||
allow_assign($roleid, 3);
|
||||
|
||||
// Call the external function.
|
||||
enrol_manual_external::enrol_users(array(
|
||||
array('roleid' => 3, 'userid' => $user1->id, 'courseid' => $course->id),
|
||||
array('roleid' => 3, 'userid' => $user2->id, 'courseid' => $course->id)
|
||||
array('roleid' => 3, 'userid' => $user1->id, 'courseid' => $course1->id),
|
||||
array('roleid' => 3, 'userid' => $user2->id, 'courseid' => $course1->id),
|
||||
));
|
||||
|
||||
// Check we retrieve the good total number of enrolled users.
|
||||
require_once($CFG->dirroot . '/enrol/externallib.php');
|
||||
$enrolledusers = core_enrol_external::get_enrolled_users($course->id);
|
||||
$this->assertEquals(2, count($enrolledusers));
|
||||
$this->assertEquals(2, $DB->count_records('user_enrolments', array('enrolid' => $instance1->id)));
|
||||
$this->assertEquals(0, $DB->count_records('user_enrolments', array('enrolid' => $instance2->id)));
|
||||
$this->assertTrue(is_enrolled($context1, $user1));
|
||||
$this->assertTrue(is_enrolled($context1, $user2));
|
||||
|
||||
// Call without required capability.
|
||||
$this->unassignUserCapability('enrol/manual:enrol', $context->id, $roleid);
|
||||
$this->setExpectedException('moodle_exception');
|
||||
$categories = enrol_manual_external::enrol_users($course->id);
|
||||
$DB->delete_records('user_enrolments');
|
||||
$this->unassignUserCapability('enrol/manual:enrol', $context1->id, $roleid);
|
||||
try {
|
||||
enrol_manual_external::enrol_users(array(
|
||||
array('roleid' => 3, 'userid' => $user1->id, 'courseid' => $course1->id),
|
||||
));
|
||||
$this->fail('Exception expected if not having capability to enrol');
|
||||
} catch (moodle_exception $e) {
|
||||
$this->assertInstanceOf('required_capability_exception', $e);
|
||||
$this->assertSame('nopermissions', $e->errorcode);
|
||||
}
|
||||
$this->assignUserCapability('enrol/manual:enrol', $context1->id, $roleid);
|
||||
$this->assertEquals(0, $DB->count_records('user_enrolments'));
|
||||
|
||||
// Call with forbidden role.
|
||||
try {
|
||||
enrol_manual_external::enrol_users(array(
|
||||
array('roleid' => 1, 'userid' => $user1->id, 'courseid' => $course1->id),
|
||||
));
|
||||
$this->fail('Exception expected if not allowed to assign role.');
|
||||
} catch (moodle_exception $e) {
|
||||
$this->assertSame('wsusercannotassign', $e->errorcode);
|
||||
}
|
||||
$this->assertEquals(0, $DB->count_records('user_enrolments'));
|
||||
|
||||
// Call for course without manual instance.
|
||||
$DB->delete_records('user_enrolments');
|
||||
$DB->delete_records('enrol', array('courseid' => $course2->id));
|
||||
try {
|
||||
enrol_manual_external::enrol_users(array(
|
||||
array('roleid' => 3, 'userid' => $user1->id, 'courseid' => $course1->id),
|
||||
array('roleid' => 3, 'userid' => $user1->id, 'courseid' => $course2->id),
|
||||
));
|
||||
$this->fail('Exception expected if course does not have manual instance');
|
||||
} catch (moodle_exception $e) {
|
||||
$this->assertSame('wsnoinstance', $e->errorcode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +43,8 @@ class core_enrol_externallib_testcase extends externallib_advanced_testcase {
|
||||
$course = self::getDataGenerator()->create_course();
|
||||
$user1 = self::getDataGenerator()->create_user();
|
||||
$user2 = self::getDataGenerator()->create_user();
|
||||
$user3 = self::getDataGenerator()->create_user();
|
||||
$this->setUser($user3);
|
||||
|
||||
// Set the required capabilities by the external function.
|
||||
$context = context_course::instance($course->id);
|
||||
@@ -52,7 +54,7 @@ class core_enrol_externallib_testcase extends externallib_advanced_testcase {
|
||||
// Enrol the users in the course.
|
||||
$this->getDataGenerator()->enrol_user($user1->id, $course->id, $roleid, 'manual');
|
||||
$this->getDataGenerator()->enrol_user($user2->id, $course->id, $roleid, 'manual');
|
||||
$this->getDataGenerator()->enrol_user($USER->id, $course->id, $roleid, 'manual');
|
||||
$this->getDataGenerator()->enrol_user($user3->id, $course->id, $roleid, 'manual');
|
||||
|
||||
// Call the external function.
|
||||
$enrolledusers = core_enrol_external::get_enrolled_users($course->id);
|
||||
@@ -60,8 +62,25 @@ class core_enrol_externallib_testcase extends externallib_advanced_testcase {
|
||||
// We need to execute the return values cleaning process to simulate the web service server.
|
||||
$enrolledusers = external_api::clean_returnvalue(core_enrol_external::get_enrolled_users_returns(), $enrolledusers);
|
||||
|
||||
// Check we retrieve the good total number of enrolled users.
|
||||
// Check the result set.
|
||||
$this->assertEquals(3, count($enrolledusers));
|
||||
$this->assertArrayHasKey('email', $enrolledusers[0]);
|
||||
|
||||
// Call the function with some parameters set.
|
||||
$enrolledusers = core_enrol_external::get_enrolled_users($course->id, array(
|
||||
array('name' => 'limitfrom', 'value' => 2),
|
||||
array('name' => 'limitnumber', 'value' => 1),
|
||||
array('name' => 'userfields', 'value' => 'id')
|
||||
));
|
||||
|
||||
// We need to execute the return values cleaning process to simulate the web service server.
|
||||
$enrolledusers = external_api::clean_returnvalue(core_enrol_external::get_enrolled_users_returns(), $enrolledusers);
|
||||
|
||||
// Check the result set, we should only get the 3rd result, which is $user3.
|
||||
$this->assertCount(1, $enrolledusers);
|
||||
$this->assertEquals($user3->id, $enrolledusers[0]['id']);
|
||||
$this->assertArrayHasKey('id', $enrolledusers[0]);
|
||||
$this->assertArrayNotHasKey('email', $enrolledusers[0]);
|
||||
|
||||
// Call without required capability.
|
||||
$this->unassignUserCapability('moodle/course:viewparticipants', $context->id, $roleid);
|
||||
@@ -110,6 +129,8 @@ class core_enrol_externallib_testcase extends externallib_advanced_testcase {
|
||||
|
||||
$this->resetAfterTest(true);
|
||||
|
||||
$user1 = $this->getDataGenerator()->create_user();
|
||||
|
||||
$coursedata['idnumber'] = 'idnumbercourse1';
|
||||
$coursedata['fullname'] = 'Lightwork Course 1';
|
||||
$coursedata['summary'] = 'Lightwork Course 1 description';
|
||||
@@ -122,14 +143,20 @@ class core_enrol_externallib_testcase extends externallib_advanced_testcase {
|
||||
$manual_enrol_data['courseid'] = $course1->id;
|
||||
$enrolid = $DB->insert_record('enrol', $manual_enrol_data);
|
||||
|
||||
// Create the user and give them capabilities in the course context.
|
||||
// Create the users and give them capabilities in the course context.
|
||||
$context = context_course::instance($course1->id);
|
||||
$roleid = $this->assignUserCapability('moodle/course:viewparticipants', $context->id, 3);
|
||||
|
||||
// Create a student.
|
||||
$student1 = self::getDataGenerator()->create_user();
|
||||
// Create 2 students.
|
||||
$student1 = self::getDataGenerator()->create_user();
|
||||
$student2 = self::getDataGenerator()->create_user();
|
||||
|
||||
// Enrol both the user and the student in the course.
|
||||
// Give the capability to student2.
|
||||
assign_capability('moodle/course:viewparticipants', CAP_ALLOW, 3, $context->id);
|
||||
role_assign(3, $student2->id, $context->id);
|
||||
accesslib_clear_all_caches_for_unit_testing();
|
||||
|
||||
// Enrol both the user and the students in the course.
|
||||
$user_enrolment_data['status'] = 0;
|
||||
$user_enrolment_data['enrolid'] = $enrolid;
|
||||
$user_enrolment_data['userid'] = $USER->id;
|
||||
@@ -140,8 +167,13 @@ class core_enrol_externallib_testcase extends externallib_advanced_testcase {
|
||||
$user_enrolment_data['userid'] = $student1->id;
|
||||
$DB->insert_record('user_enrolments', $user_enrolment_data);
|
||||
|
||||
$params = array("coursecapabilities" =>array
|
||||
('courseid' => $course1->id, 'capabilities' => array('moodle/course:viewparticipants')));
|
||||
$user_enrolment_data['status'] = 0;
|
||||
$user_enrolment_data['enrolid'] = $enrolid;
|
||||
$user_enrolment_data['userid'] = $student2->id;
|
||||
$DB->insert_record('user_enrolments', $user_enrolment_data);
|
||||
|
||||
$params = array("coursecapabilities" => array('courseid' => $course1->id,
|
||||
'capabilities' => array('moodle/course:viewparticipants')));
|
||||
$options = array();
|
||||
$result = core_enrol_external::get_enrolled_users_with_capability($params, $options);
|
||||
|
||||
@@ -152,8 +184,31 @@ class core_enrol_externallib_testcase extends externallib_advanced_testcase {
|
||||
$expecteduserlist = $result[0];
|
||||
$this->assertEquals($course1->id, $expecteduserlist['courseid']);
|
||||
$this->assertEquals('moodle/course:viewparticipants', $expecteduserlist['capability']);
|
||||
$this->assertEquals(1, count($expecteduserlist['users']));
|
||||
$this->assertEquals(2, count($expecteduserlist['users']));
|
||||
|
||||
// Now doing the query again with options.
|
||||
$params = array(
|
||||
"coursecapabilities" => array(
|
||||
'courseid' => $course1->id,
|
||||
'capabilities' => array('moodle/course:viewparticipants')
|
||||
)
|
||||
);
|
||||
$options = array(
|
||||
array('name' => 'limitfrom', 'value' => 1),
|
||||
array('name' => 'limitnumber', 'value' => 1),
|
||||
array('name' => 'userfields', 'value' => 'id')
|
||||
);
|
||||
|
||||
$result = core_enrol_external::get_enrolled_users_with_capability($params, $options);
|
||||
|
||||
// We need to execute the return values cleaning process to simulate the web service server.
|
||||
$result = external_api::clean_returnvalue(core_enrol_external::get_enrolled_users_with_capability_returns(), $result);
|
||||
|
||||
// Check an array containing the expected user for the course capability is returned.
|
||||
$expecteduserlist = $result[0]['users'];
|
||||
$expecteduser = reset($expecteduserlist);
|
||||
$this->assertEquals(1, count($expecteduserlist));
|
||||
$this->assertEquals($student2->id, $expecteduser['id']);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@core @core_files @_only_local
|
||||
@core @core_files
|
||||
Feature: Course files
|
||||
In order to add legacy files
|
||||
As a user
|
||||
|
||||
@@ -223,7 +223,7 @@ class core_files_externallib_testcase extends advanced_testcase {
|
||||
// Insert the information about the file.
|
||||
$contentid = $DB->insert_record('data_content', $datacontent);
|
||||
// Required information for uploading a file.
|
||||
$context = context_module::instance($module->id);
|
||||
$context = context_module::instance($module->cmid);
|
||||
$usercontext = context_user::instance($USER->id);
|
||||
$component = 'mod_data';
|
||||
$filearea = 'content';
|
||||
@@ -251,6 +251,7 @@ class core_files_externallib_testcase extends advanced_testcase {
|
||||
$testfilelisting = core_files_external::get_files($context->id, $component, $filearea, $itemid, '/', $filename);
|
||||
|
||||
// With the information that we have provided we should get an object exactly like the one below.
|
||||
$coursecontext = context_course::instance($course->id);
|
||||
$testdata = array();
|
||||
$testdata['parents'] = array();
|
||||
$testdata['parents']['0'] = array('contextid' => 1,
|
||||
@@ -265,43 +266,43 @@ class core_files_externallib_testcase extends advanced_testcase {
|
||||
'itemid' => null,
|
||||
'filepath' => null,
|
||||
'filename' => 'Miscellaneous');
|
||||
$testdata['parents']['2'] = array('contextid' => 15,
|
||||
$testdata['parents']['2'] = array('contextid' => $coursecontext->id,
|
||||
'component' => null,
|
||||
'filearea' => null,
|
||||
'itemid' => null,
|
||||
'filepath' => null,
|
||||
'filename' => 'Test course 1');
|
||||
$testdata['parents']['3'] = array('contextid' => 20,
|
||||
$testdata['parents']['3'] = array('contextid' => $context->id,
|
||||
'component' => null,
|
||||
'filearea' => null,
|
||||
'itemid' => null,
|
||||
'filepath' => null,
|
||||
'filename' => 'Mod data upload test (Database)');
|
||||
$testdata['parents']['4'] = array('contextid' => 20,
|
||||
$testdata['parents']['4'] = array('contextid' => $context->id,
|
||||
'component' => 'mod_data',
|
||||
'filearea' => 'content',
|
||||
'itemid' => null,
|
||||
'filepath' => null,
|
||||
'filename' => 'Fields');
|
||||
$testdata['files'] = array();
|
||||
$testdata['files']['0'] = array('contextid' => 20,
|
||||
$testdata['files']['0'] = array('contextid' => $context->id,
|
||||
'component' => 'mod_data',
|
||||
'filearea' => 'content',
|
||||
'itemid' => '1',
|
||||
'itemid' => $itemid,
|
||||
'filepath' => '/',
|
||||
'filename' => 'Simple4.txt',
|
||||
'url' => 'http://www.example.com/moodle/pluginfile.php/20/mod_data/content/1/Simple4.txt',
|
||||
'url' => 'http://www.example.com/moodle/pluginfile.php/'.$context->id.'/mod_data/content/'.$itemid.'/Simple4.txt',
|
||||
'isdir' => false,
|
||||
'timemodified' => $timemodified);
|
||||
// Make sure that they are the same.
|
||||
$this->assertEquals($testfilelisting, $testdata);
|
||||
$this->assertEquals($testdata, $testfilelisting);
|
||||
|
||||
// Try again but without the context. Minus one signals the function to use other variables to obtain the context.
|
||||
$nocontext = -1;
|
||||
$modified = 0;
|
||||
// Context level and instance ID are used to determine what the context is.
|
||||
$contextlevel = 'module';
|
||||
$instanceid = $module->id;
|
||||
$instanceid = $module->cmid;
|
||||
$testfilelisting = core_files_external::get_files($nocontext, $component, $filearea, $itemid, '/', $filename, $modified, $contextlevel, $instanceid);
|
||||
$this->assertEquals($testfilelisting, $testdata);
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ class backup_gradingform_guide_plugin extends backup_gradingform_plugin {
|
||||
|
||||
$pluginwrapper->add_child($criteria);
|
||||
$criteria->add_child($criterion);
|
||||
$criteria->add_child($comments);
|
||||
$pluginwrapper->add_child($comments);
|
||||
$comments->add_child($comment);
|
||||
|
||||
// Set sources to populate the data.
|
||||
|
||||
@@ -48,6 +48,11 @@ class restore_gradingform_guide_plugin extends restore_gradingform_plugin {
|
||||
$paths[] = new restore_path_element('gradingform_guide_comment',
|
||||
$this->get_pathfor('/guidecomments/guidecomment'));
|
||||
|
||||
// MDL-37714: Correctly locate frequent used comments in both the
|
||||
// current and incorrect old format.
|
||||
$paths[] = new restore_path_element('gradingform_guide_comment_legacy',
|
||||
$this->get_pathfor('/guidecriteria/guidecomments/guidecomment'));
|
||||
|
||||
return $paths;
|
||||
}
|
||||
|
||||
@@ -99,6 +104,20 @@ class restore_gradingform_guide_plugin extends restore_gradingform_plugin {
|
||||
$DB->insert_record('gradingform_guide_comments', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes comments element data
|
||||
*
|
||||
* @param array|stdClass $data The data to insert as a comment
|
||||
*/
|
||||
public function process_gradingform_guide_comment_legacy($data) {
|
||||
global $DB;
|
||||
|
||||
$data = (object)$data;
|
||||
$data->definitionid = $this->get_new_parentid('grading_definition');
|
||||
|
||||
$DB->insert_record('gradingform_guide_comments', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes filling element data
|
||||
*
|
||||
|
||||
@@ -11,8 +11,8 @@
|
||||
.gradingform_guide .criterion .remark {vertical-align: top;}
|
||||
|
||||
.gradingform_guide.editor .criterion .controls,
|
||||
.gradingform_guide .criterion .description,
|
||||
.gradingform_guide .criterion .remark {padding:3px;}
|
||||
.gradingform_guide.editor .criterion .description,
|
||||
.gradingform_guide.editor .criterion .remark {padding:3px;}
|
||||
|
||||
.gradingform_guide .criteria {height:100%;}
|
||||
.gradingform_guide .criterion {border:1px solid #DDD;overflow: hidden;}
|
||||
@@ -55,13 +55,35 @@
|
||||
.gradingform_guide.editor .hiddenelement {display:none;}
|
||||
.gradingform_guide.editor .pseudotablink {background-color:transparent;border:0 solid;height:1px;width:1px;color:transparent;padding:0;margin:0;position:relative;float:right;}
|
||||
|
||||
.gradingform_guide .markingguidecomment {cursor: pointer;}
|
||||
.jsenabled .gradingform_guide .markingguidecomment:before {content: url([[pix:t/add]]);padding-right:2px;}
|
||||
.jsenabled .gradingform_guide .markingguidecomment {cursor: pointer;}
|
||||
.jsenabled .gradingform_guide .markingguidecomment:before {
|
||||
content: url([[pix:t/add]]);
|
||||
padding-right:2px;
|
||||
}
|
||||
.dir-rtl.jsenabled .gradingform_guide .markingguidecomment:before {
|
||||
padding-right: 0;
|
||||
padding-left: 2px;
|
||||
}
|
||||
.gradingform_guide .commentheader {font-weight:bold;font-size:1.1em;padding-bottom:5px;}
|
||||
|
||||
.jsenabled .gradingform_guide .criterionnamelabel {display: none;}
|
||||
.jsenabled .gradingform_guide .criterionshortname {font-weight:bold;}
|
||||
.gradingform_guide table {width: 100%}
|
||||
.gradingform_guide .descriptionreadonly {
|
||||
vertical-align: top;
|
||||
}
|
||||
.gradingform_guide .criteriondescriptionmarkers {width: 300px;}
|
||||
.gradingform_guide .markingguideremark {width: 100%;}
|
||||
.gradingform_guide .criteriondescriptionscore {display: inline;}
|
||||
.gradingform_guide .markingguideremark {
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.gradingform_guide .criteriondescriptionscore {display: inline;}
|
||||
.gradingform_guide .score label {
|
||||
display: block;
|
||||
}
|
||||
.gradingform_guide .score input {
|
||||
margin: 0;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,490 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Steps definitions for rubrics.
|
||||
*
|
||||
* @package gradingform_rubric
|
||||
* @category test
|
||||
* @copyright 2013 David Monllaó
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
// NOTE: no MOODLE_INTERNAL test here, this file may be required by behat before including /config.php.
|
||||
|
||||
require_once(__DIR__ . '/../../../../../../lib/behat/behat_base.php');
|
||||
|
||||
use Behat\Gherkin\Node\TableNode as TableNode,
|
||||
Behat\Behat\Context\Step\Given as Given,
|
||||
Behat\Behat\Context\Step\When as When,
|
||||
Behat\Behat\Context\Step\Then as Then,
|
||||
Behat\Mink\Exception\ElementNotFoundException as ElementNotFoundException,
|
||||
Behat\Mink\Exception\ExpectationException as ExpectationException;
|
||||
|
||||
/**
|
||||
* Steps definitions to help with rubrics.
|
||||
*
|
||||
* @package gradingform_rubric
|
||||
* @category test
|
||||
* @copyright 2013 David Monllaó
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class behat_gradingform_rubric extends behat_base {
|
||||
|
||||
/**
|
||||
* @var The number of levels added by default when a rubric is created.
|
||||
*/
|
||||
const DEFAULT_RUBRIC_LEVELS = 3;
|
||||
|
||||
/**
|
||||
* Defines the rubric with the provided data, following rubric's definition grid cells.
|
||||
*
|
||||
* This method fills the rubric of the rubric definition
|
||||
* form; the provided TableNode should contain one row for
|
||||
* each criterion and each cell of the row should contain:
|
||||
* # Criterion description
|
||||
* # Criterion level 1 name
|
||||
* # Criterion level 1 points
|
||||
* # Criterion level 2 name
|
||||
* # Criterion level 2 points
|
||||
* # Criterion level 3 .....
|
||||
*
|
||||
* Works with both JS and non-JS.
|
||||
*
|
||||
* @When /^I define the following rubric:$/
|
||||
* @throws ExpectationException
|
||||
* @param TableNode $rubric
|
||||
*/
|
||||
public function i_define_the_following_rubric(TableNode $rubric) {
|
||||
|
||||
// Being a smart method is nothing good when we talk about step definitions, in
|
||||
// this case we didn't have any other options as there are no labels no elements
|
||||
// id we can point to without having to "calculate" them.
|
||||
|
||||
$steptableinfo = '| criterion description | level1 name | level1 points | level2 name | level2 points | ...';
|
||||
|
||||
$criteria = $rubric->getRows();
|
||||
|
||||
$addcriterionbutton = $this->find_button(get_string('addcriterion', 'gradingform_rubric'));
|
||||
|
||||
// Cleaning the current ones.
|
||||
$deletebuttons = $this->find_all('css', "input[title='" . get_string('criteriondelete', 'gradingform_rubric') . "']");
|
||||
if ($deletebuttons) {
|
||||
|
||||
// We should reverse the deletebuttons because otherwise once we delete
|
||||
// the first one the DOM will change and the [X] one will not exist anymore.
|
||||
$deletebuttons = array_reverse($deletebuttons, true);
|
||||
foreach ($deletebuttons as $button) {
|
||||
$this->click_and_confirm($button);
|
||||
}
|
||||
}
|
||||
|
||||
// The level number (NEWID$N) is not reset after each criterion.
|
||||
$levelnumber = 1;
|
||||
|
||||
// The next criterion is created with the same number of levels than the last criterion.
|
||||
$defaultnumberoflevels = self::DEFAULT_RUBRIC_LEVELS;
|
||||
|
||||
if ($criteria) {
|
||||
foreach ($criteria as $criterionit => $criterion) {
|
||||
|
||||
// Checking the number of cells.
|
||||
if (count($criterion) % 2 === 0) {
|
||||
throw new ExpectationException(
|
||||
'The criterion levels should contain both definition and points, follow this format:' . $steptableinfo,
|
||||
$this->getSession()
|
||||
);
|
||||
}
|
||||
|
||||
// Minimum 2 levels per criterion.
|
||||
// description + definition1 + score1 + definition2 + score2 = 5.
|
||||
if (count($criterion) < 5) {
|
||||
throw new ExpectationException(
|
||||
get_string('err_mintwolevels', 'gradingform_rubric'),
|
||||
$this->getSession()
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
// Add new criterion.
|
||||
$addcriterionbutton->click();
|
||||
|
||||
$criterionroot = 'rubric[criteria][NEWID' . ($criterionit + 1) . ']';
|
||||
|
||||
// Getting the criterion description, this one is visible by default.
|
||||
$this->set_rubric_field_value($criterionroot . '[description]', array_shift($criterion), true);
|
||||
|
||||
// When JS is disabled each criterion's levels name numbers starts from 0.
|
||||
if (!$this->running_javascript()) {
|
||||
$levelnumber = 0;
|
||||
}
|
||||
|
||||
// Setting the correct number of levels.
|
||||
$nlevels = count($criterion) / 2;
|
||||
if ($nlevels < $defaultnumberoflevels) {
|
||||
|
||||
// Removing levels if there are too much levels.
|
||||
// When we add a new level the NEWID$N is increased from the last criterion.
|
||||
$lastcriteriondefaultlevel = $defaultnumberoflevels + $levelnumber - 1;
|
||||
$lastcriterionlevel = $nlevels + $levelnumber - 1;
|
||||
for ($i = $lastcriteriondefaultlevel; $i > $lastcriterionlevel; $i--) {
|
||||
|
||||
// If JS is disabled seems that new levels are not added.
|
||||
if ($this->running_javascript()) {
|
||||
$deletelevel = $this->find_button($criterionroot . '[levels][NEWID' . $i . '][delete]');
|
||||
$this->click_and_confirm($deletelevel);
|
||||
|
||||
} else {
|
||||
// Only if the level exists.
|
||||
$buttonname = $criterionroot . '[levels][NEWID' . $i . '][delete]';
|
||||
if ($deletelevel = $this->getSession()->getPage()->findButton($buttonname)) {
|
||||
$deletelevel->click();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if ($nlevels > $defaultnumberoflevels) {
|
||||
// Adding levels if we don't have enough.
|
||||
$addlevel = $this->find_button($criterionroot . '[levels][addlevel]');
|
||||
for ($i = ($defaultnumberoflevels + 1); $i <= $nlevels; $i++) {
|
||||
$addlevel->click();
|
||||
}
|
||||
}
|
||||
|
||||
// Updating it.
|
||||
if ($nlevels > self::DEFAULT_RUBRIC_LEVELS) {
|
||||
$defaultnumberoflevels = $nlevels;
|
||||
} else {
|
||||
// If it is less than the default value it sets it to
|
||||
// the default value.
|
||||
$defaultnumberoflevels = self::DEFAULT_RUBRIC_LEVELS;
|
||||
}
|
||||
|
||||
foreach ($criterion as $i => $value) {
|
||||
|
||||
$levelroot = $criterionroot . '[levels][NEWID' . $levelnumber . ']';
|
||||
|
||||
if ($i % 2 === 0) {
|
||||
// Pairs are the definitions.
|
||||
$fieldname = $levelroot . '[definition]';
|
||||
$this->set_rubric_field_value($fieldname, $value);
|
||||
|
||||
} else {
|
||||
// Odds are the points.
|
||||
|
||||
// Checking it now, we would need to remove it if we are testing the form validations...
|
||||
if (!is_numeric($value)) {
|
||||
throw new ExpectationException(
|
||||
'The points cells should contain numeric values, follow this format: ' . $steptableinfo,
|
||||
$this->getSession()
|
||||
);
|
||||
}
|
||||
|
||||
$fieldname = $levelroot . '[score]';
|
||||
$this->set_rubric_field_value($fieldname, $value, true);
|
||||
|
||||
// Increase the level by one every 2 cells.
|
||||
$levelnumber++;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces a value from the specified criterion. You can use it when editing rubrics, to set both name or points.
|
||||
*
|
||||
* @When /^I replace "(?P<current_value_string>(?:[^"]|\\")*)" rubric level with "(?P<value_string>(?:[^"]|\\")*)" in "(?P<criterion_string>(?:[^"]|\\")*)" criterion$/
|
||||
* @throws ElementNotFoundException
|
||||
* @param string $currentvalue
|
||||
* @param string $value
|
||||
* @param string $criterionname
|
||||
* @return Given[]
|
||||
*/
|
||||
public function i_replace_rubric_level_with($currentvalue, $value, $criterionname) {
|
||||
|
||||
$currentvalueliteral = $this->getSession()->getSelectorsHandler()->xpathLiteral($currentvalue);
|
||||
$criterionliteral = $this->getSession()->getSelectorsHandler()->xpathLiteral($criterionname);
|
||||
|
||||
$criterionxpath = "//div[@id='rubric-rubric']" .
|
||||
"/descendant::td[contains(concat(' ', normalize-space(@class), ' '), ' description ')]";
|
||||
// It differs between JS on/off.
|
||||
if ($this->running_javascript()) {
|
||||
$criterionxpath .= "/descendant::span[@class='textvalue'][text()=$criterionliteral]" .
|
||||
"/ancestor::tr[contains(concat(' ', normalize-space(@class), ' '), ' criterion ')]";
|
||||
} else {
|
||||
$criterionxpath .= "/descendant::textarea[text()=$criterionliteral]" .
|
||||
"/ancestor::tr[contains(concat(' ', normalize-space(@class), ' '), ' criterion ')]";
|
||||
}
|
||||
|
||||
$inputxpath = $criterionxpath .
|
||||
"/descendant::input[@type='text'][@value=$currentvalueliteral]";
|
||||
$textareaxpath = $criterionxpath .
|
||||
"/descendant::textarea[text()=$currentvalueliteral]";
|
||||
|
||||
if ($this->running_javascript()) {
|
||||
|
||||
$spansufix = "/ancestor::div[@class='level-wrapper']" .
|
||||
"/descendant::div[@class='definition']" .
|
||||
"/descendant::span[@class='textvalue']";
|
||||
|
||||
// Expanding the level input boxes.
|
||||
$spannode = $this->find('xpath', $inputxpath . $spansufix . '|' . $textareaxpath . $spansufix);
|
||||
$spannode->click();
|
||||
|
||||
$inputfield = $this->find('xpath', $inputxpath . '|' . $textareaxpath);
|
||||
$inputfield->setValue($value);
|
||||
|
||||
} else {
|
||||
$fieldnode = $this->find('xpath', $inputxpath . '|' . $textareaxpath);
|
||||
$this->set_rubric_field_value($fieldnode->getAttribute('name'), $value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Grades filling the current page rubric. Set one line per criterion and for each criterion set "| Criterion name | Points | Remark |".
|
||||
*
|
||||
* @When /^I grade by filling the rubric with:$/
|
||||
*
|
||||
* @throws ExpectationException
|
||||
* @param TableNode $rubric
|
||||
* @return void
|
||||
*/
|
||||
public function i_grade_by_filling_the_rubric_with(TableNode $rubric) {
|
||||
|
||||
$criteria = $rubric->getRowsHash();
|
||||
|
||||
$stepusage = '"I grade by filling the rubric with:" step needs you to provide a table where each row is a criterion' .
|
||||
' and each criterion has 3 different values: | Criterion name | Number of points | Remark text |';
|
||||
|
||||
// To fill with the steps to execute.
|
||||
$steps = array();
|
||||
|
||||
// First element -> name, second -> points, third -> Remark.
|
||||
foreach ($criteria as $name => $criterion) {
|
||||
|
||||
// We only expect the points and the remark, as the criterion name is $name.
|
||||
if (count($criterion) !== 2) {
|
||||
throw new ExpectationException($stepusage, $this->getSession());
|
||||
}
|
||||
|
||||
// Numeric value here.
|
||||
$points = $criterion[0];
|
||||
if (!is_numeric($points)) {
|
||||
throw new ExpectationException($stepusage, $this->getSession());
|
||||
}
|
||||
|
||||
// Selecting a value.
|
||||
// When JS is disabled there are radio options, with JS enabled divs.
|
||||
$selectedlevelxpath = $this->get_level_xpath($points);
|
||||
if ($this->running_javascript()) {
|
||||
|
||||
// Only clicking on the selected level if it was not already selected.
|
||||
$levelnode = $this->find('xpath', $selectedlevelxpath);
|
||||
|
||||
// Using in_array() as there are only a few elements.
|
||||
if (!in_array('checked', explode(' ', $levelnode->getAttribute('class')))) {
|
||||
$steps[] = new Given('I click on "' . $selectedlevelxpath . '" "xpath_element" in the "' .
|
||||
$this->escape($name) . '" "table_row"');
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// Getting the name of the field.
|
||||
$radioxpath = $this->get_criterion_xpath($name) .
|
||||
$selectedlevelxpath . "/descendant::input[@type='radio']";
|
||||
$radionode = $this->find('xpath', $radioxpath);
|
||||
// TODO MDL-43738: Change setValue() to use the generic set_value()
|
||||
// which will delegate the process to the field type.
|
||||
$radionode->setValue($radionode->getAttribute('value'));
|
||||
}
|
||||
|
||||
// Setting the remark.
|
||||
|
||||
// First we need to get the textarea name, then we can set the value.
|
||||
$textarea = $this->get_node_in_container('css_element', 'textarea', 'table_row', $name);
|
||||
$steps[] = new Given('I fill in "' . $textarea->getAttribute('name') . '" with "' . $criterion[1] . '"');
|
||||
}
|
||||
|
||||
return $steps;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that the level was previously selected and the user changed to another level.
|
||||
*
|
||||
* @Then /^the level with "(?P<points_number>\d+)" points was previously selected for the rubric criterion "(?P<criterion_name_string>(?:[^"]|\\")*)"$/
|
||||
* @throws ExpectationException
|
||||
* @param string $criterionname
|
||||
* @param int $points
|
||||
* @return void
|
||||
*/
|
||||
public function the_level_with_points_was_previously_selected_for_the_rubric_criterion($points, $criterionname) {
|
||||
|
||||
$levelxpath = $this->get_criterion_xpath($criterionname) .
|
||||
$this->get_level_xpath($points) .
|
||||
"[contains(concat(' ', normalize-space(@class), ' '), ' currentchecked ')]";
|
||||
|
||||
// Works both for JS and non-JS.
|
||||
// - JS: Class -> checked is there when is marked as green.
|
||||
// - Non-JS: When editing a rubric definition, there are radio inputs and when viewing a
|
||||
// grade @class contains checked.
|
||||
$levelxpath .= "[not(contains(concat(' ', normalize-space(@class), ' '), ' checked '))]" .
|
||||
"[not(/descendant::input[@type='radio'][@checked!='checked'])]";
|
||||
|
||||
try {
|
||||
$this->find('xpath', $levelxpath);
|
||||
} catch (ElementNotFoundException $e) {
|
||||
throw new ExpectationException('"' . $points . '" points level was not previously selected', $this->getSession());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that the level is currently selected. Works both when grading rubrics and viewing graded rubrics.
|
||||
*
|
||||
* @Then /^the level with "(?P<points_number>\d+)" points is selected for the rubric criterion "(?P<criterion_name_string>(?:[^"]|\\")*)"$/
|
||||
* @throws ExpectationException
|
||||
* @param string $criterionname
|
||||
* @param int $points
|
||||
* @return void
|
||||
*/
|
||||
public function the_level_with_points_is_selected_for_the_rubric_criterion($points, $criterionname) {
|
||||
|
||||
$levelxpath = $this->get_criterion_xpath($criterionname) .
|
||||
$this->get_level_xpath($points);
|
||||
|
||||
// Works both for JS and non-JS.
|
||||
// - JS: Class -> checked is there when is marked as green.
|
||||
// - Non-JS: When editing a rubric definition, there are radio inputs and when viewing a
|
||||
// grade @class contains checked.
|
||||
$levelxpath .= "[" .
|
||||
"contains(concat(' ', normalize-space(@class), ' '), ' checked ')" .
|
||||
" or " .
|
||||
"/descendant::input[@type='radio'][@checked='checked']" .
|
||||
"]";
|
||||
|
||||
try {
|
||||
$this->find('xpath', $levelxpath);
|
||||
} catch (ElementNotFoundException $e) {
|
||||
throw new ExpectationException('"' . $points . '" points level is not selected', $this->getSession());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that the level is not currently selected. Works both when grading rubrics and viewing graded rubrics.
|
||||
*
|
||||
* @Then /^the level with "(?P<points_number>\d+)" points is not selected for the rubric criterion "(?P<criterion_name_string>(?:[^"]|\\")*)"$/
|
||||
* @throws ExpectationException
|
||||
* @param string $criterionname
|
||||
* @param int $points
|
||||
* @return void
|
||||
*/
|
||||
public function the_level_with_points_is_not_selected_for_the_rubric_criterion($points, $criterionname) {
|
||||
|
||||
$levelxpath = $this->get_criterion_xpath($criterionname) .
|
||||
$this->get_level_xpath($points);
|
||||
|
||||
// Works both for JS and non-JS.
|
||||
// - JS: Class -> checked is there when is marked as green.
|
||||
// - Non-JS: When editing a rubric definition, there are radio inputs and when viewing a
|
||||
// grade @class contains checked.
|
||||
$levelxpath .= "[not(contains(concat(' ', normalize-space(@class), ' '), ' checked '))]" .
|
||||
"[./descendant::input[@type='radio'][@checked!='checked'] or not(./descendant::input[@type='radio'])]";
|
||||
|
||||
try {
|
||||
$this->find('xpath', $levelxpath);
|
||||
} catch (ElementNotFoundException $e) {
|
||||
throw new ExpectationException('"' . $points . '" points level is selected', $this->getSession());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Makes a hidden rubric field visible (if necessary) and sets a value on it.
|
||||
*
|
||||
* @param string $name The name of the field
|
||||
* @param string $value The value to set
|
||||
* @param bool $visible
|
||||
* @return void
|
||||
*/
|
||||
protected function set_rubric_field_value($name, $value, $visible = false) {
|
||||
|
||||
// Fields are hidden by default.
|
||||
if ($this->running_javascript() == true && $visible === false) {
|
||||
$xpath = "//*[@name='$name']/following-sibling::*[contains(concat(' ', normalize-space(@class), ' '), ' plainvalue ')]";
|
||||
$textnode = $this->find('xpath', $xpath);
|
||||
$textnode->click();
|
||||
}
|
||||
|
||||
// Set the value now.
|
||||
$description = $this->find_field($name);
|
||||
$description->setValue($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs click confirming the action.
|
||||
*
|
||||
* @param NodeElement $node
|
||||
* @return void
|
||||
*/
|
||||
protected function click_and_confirm($node) {
|
||||
|
||||
// Clicks to perform the action.
|
||||
$node->click();
|
||||
|
||||
// Confirms the delete.
|
||||
if ($this->running_javascript()) {
|
||||
$confirmbutton = $this->get_node_in_container(
|
||||
'button',
|
||||
get_string('yes'),
|
||||
'dialogue',
|
||||
get_string('confirmation', 'admin')
|
||||
);
|
||||
$confirmbutton->click();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the xpath representing a selected level.
|
||||
*
|
||||
* It is not including the path to the criterion.
|
||||
*
|
||||
* It is the xpath when grading a rubric or viewing a rubric,
|
||||
* it is not the same xpath when editing a rubric.
|
||||
*
|
||||
* @param int $points
|
||||
* @return string
|
||||
*/
|
||||
protected function get_level_xpath($points) {
|
||||
return "//td[contains(concat(' ', normalize-space(@class), ' '), ' level ')]" .
|
||||
"[./descendant::span[@class='scorevalue'][text()='$points']]";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the xpath representing the selected criterion.
|
||||
*
|
||||
* It is the xpath when grading a rubric or viewing a rubric,
|
||||
* it is not the same xpath when editing a rubric.
|
||||
*
|
||||
* @param string $criterionname Literal including the criterion name.
|
||||
* @return string
|
||||
*/
|
||||
protected function get_criterion_xpath($criterionname) {
|
||||
$literal = $this->getSession()->getSelectorsHandler()->xpathLiteral($criterionname);
|
||||
return "//tr[contains(concat(' ', normalize-space(@class), ' '), ' criterion ')]" .
|
||||
"[./descendant::td[@class='description'][text()=$literal]]";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
@gradingform @gradingform_rubric
|
||||
Feature: Rubrics can be created and edited
|
||||
In order to use and refine rubrics to grade students
|
||||
As a teacher
|
||||
I need to edit previously used rubrics
|
||||
|
||||
Background:
|
||||
Given the following "users" exists:
|
||||
| username | firstname | lastname | email |
|
||||
| teacher1 | Teacher | 1 | teacher1@asd.com |
|
||||
| student1 | Student | 1 | student1@asd.com |
|
||||
And the following "courses" exists:
|
||||
| fullname | shortname | format |
|
||||
| Course 1 | C1 | topics |
|
||||
And the following "course enrolments" exists:
|
||||
| user | course | role |
|
||||
| teacher1 | C1 | editingteacher |
|
||||
| student1 | C1 | student |
|
||||
And I log in as "teacher1"
|
||||
And I follow "Course 1"
|
||||
And I turn editing mode on
|
||||
And I add a "Assignment" to section "1" and I fill the form with:
|
||||
| Assignment name | Test assignment 1 name |
|
||||
| Description | Test assignment description |
|
||||
| Grading method | Rubric |
|
||||
When I go to "Test assignment 1 name" advanced grading definition page
|
||||
# Defining a rubric.
|
||||
And I fill the moodle form with:
|
||||
| Name | Assignment 1 rubric |
|
||||
| Description | Rubric test description |
|
||||
And I define the following rubric:
|
||||
| TMP Criterion 1 | TMP Level 11 | 11 | TMP Level 12 | 12 |
|
||||
| TMP Criterion 2 | TMP Level 21 | 21 | TMP Level 22 | 22 |
|
||||
| TMP Criterion 3 | TMP Level 31 | 31 | TMP Level 32 | 32 |
|
||||
| TMP Criterion 4 | TMP Level 41 | 41 | TMP Level 42 | 42 |
|
||||
# Checking that only the last ones are saved.
|
||||
And I define the following rubric:
|
||||
| Criterion 1 | Level 11 | 1 | Level 12 | 20 | Level 13 | 40 | Level 14 | 50 |
|
||||
| Criterion 2 | Level 21 | 10 | Level 22 | 20 | Level 23 | 30 |
|
||||
| Criterion 3 | Level 31 | 5 | Level 32 | 20 |
|
||||
And I press "Save as draft"
|
||||
And I go to "Test assignment 1 name" advanced grading definition page
|
||||
And I click on "Move down" "button" in the "Criterion 1" "table_row"
|
||||
And I press "Save rubric and make it ready"
|
||||
Then I should see "Ready for use"
|
||||
# Grading two students.
|
||||
And I go to "Student 1" "Test assignment 1 name" activity advanced grading page
|
||||
And I grade by filling the rubric with:
|
||||
| Criterion 1 | 50 | Very good |
|
||||
And I press "Save changes"
|
||||
# Checking that it complains if you don't select a level for each criterion.
|
||||
And I should see "Please choose something for each criterion"
|
||||
And I grade by filling the rubric with:
|
||||
| Criterion 1 | 50 | Very good |
|
||||
| Criterion 2 | 10 | Mmmm, you can do it better |
|
||||
| Criterion 3 | 5 | Not good |
|
||||
And I complete the advanced grading form with these values:
|
||||
| Feedback comments | In general... work harder... |
|
||||
# Checking that the user grade is correct.
|
||||
And I should see "58.33" in the "Student 1" "table_row"
|
||||
# Updating the user grade.
|
||||
And I go to "Student 1" "Test assignment 1 name" activity advanced grading page
|
||||
And I grade by filling the rubric with:
|
||||
| Criterion 1 | 20 | Bad, I changed my mind |
|
||||
| Criterion 2 | 10 | Mmmm, you can do it better |
|
||||
| Criterion 3 | 5 | Not good |
|
||||
#And the level with "50" points was previously selected for the rubric criterion "Criterion 1"
|
||||
#And the level with "20" points is selected for the rubric criterion "Criterion 1"
|
||||
And I save the advanced grading form
|
||||
And I should see "22.62" in the "Student 1" "table_row"
|
||||
And I log out
|
||||
# Viewing it as a student.
|
||||
And I log in as "student1"
|
||||
And I follow "Course 1"
|
||||
And I follow "Test assignment 1 name"
|
||||
And I should see "22.62" in the ".feedback" "css_element"
|
||||
And I should see "Rubric test description" in the ".feedback" "css_element"
|
||||
And I should see "In general... work harder..."
|
||||
And the level with "10" points is selected for the rubric criterion "Criterion 2"
|
||||
And the level with "20" points is selected for the rubric criterion "Criterion 1"
|
||||
And the level with "5" points is selected for the rubric criterion "Criterion 3"
|
||||
And I log out
|
||||
And I log in as "teacher1"
|
||||
And I follow "Course 1"
|
||||
# Editing a rubric definition without regrading students.
|
||||
And I go to "Test assignment 1 name" advanced grading definition page
|
||||
And "Save as draft" "button" should not exists
|
||||
And I click on "Move up" "button" in the "Criterion 1" "table_row"
|
||||
And I replace "Level 11" rubric level with "Level 11 edited" in "Criterion 1" criterion
|
||||
And I press "Save"
|
||||
And I should see "You are about to save changes to a rubric that has already been used for grading."
|
||||
And I select "Do not mark for regrade" from "menurubricregrade"
|
||||
And I press "Continue"
|
||||
And I log out
|
||||
# Check that the student still sees the grade.
|
||||
And I log in as "student1"
|
||||
And I follow "Course 1"
|
||||
And I follow "Test assignment 1 name"
|
||||
And I should see "22.62" in the ".feedback" "css_element"
|
||||
And the level with "20" points is selected for the rubric criterion "Criterion 1"
|
||||
And I log out
|
||||
# Editing a rubric with significant changes.
|
||||
And I log in as "teacher1"
|
||||
And I follow "Course 1"
|
||||
And I go to "Test assignment 1 name" advanced grading definition page
|
||||
And I click on "Move down" "button" in the "Criterion 2" "table_row"
|
||||
And I replace "1" rubric level with "11" in "Criterion 1" criterion
|
||||
And I press "Save"
|
||||
And I should see "You are about to save significant changes to a rubric that has already been used for grading. The gradebook value will be unchanged, but the rubric will be hidden from students until their item is regraded."
|
||||
And I press "Continue"
|
||||
And I log out
|
||||
# Check that the student doesn't see the grade.
|
||||
And I log in as "student1"
|
||||
And I follow "Course 1"
|
||||
And I follow "Test assignment 1 name"
|
||||
And I should see "22.62" in the ".feedback" "css_element"
|
||||
And the level with "20" points is not selected for the rubric criterion "Criterion 1"
|
||||
And I log out
|
||||
# Regrade student.
|
||||
And I log in as "teacher1"
|
||||
And I follow "Course 1"
|
||||
And I follow "Test assignment 1 name"
|
||||
And I go to "Student 1" "Test assignment 1 name" activity advanced grading page
|
||||
And I should see "The rubric definition was changed after this student had been graded. The student can not see this rubric until you check the rubric and update the grade."
|
||||
And I save the advanced grading form
|
||||
And I log out
|
||||
# Check that the student sees the grade again.
|
||||
And I log in as "student1"
|
||||
And I follow "Course 1"
|
||||
And I follow "Test assignment 1 name"
|
||||
And I should see "12.16" in the ".feedback" "css_element"
|
||||
And the level with "20" points is not selected for the rubric criterion "Criterion 1"
|
||||
# Hide all rubric info for students
|
||||
And I log out
|
||||
And I log in as "teacher1"
|
||||
And I follow "Course 1"
|
||||
And I go to "Test assignment 1 name" advanced grading definition page
|
||||
And I uncheck "Allow users to preview rubric used in the module (otherwise rubric will only become visible after grading)"
|
||||
And I uncheck "Display rubric description during evaluation"
|
||||
And I uncheck "Display rubric description to those being graded"
|
||||
And I uncheck "Display points for each level during evaluation"
|
||||
And I uncheck "Display points for each level to those being graded"
|
||||
And I press "Save"
|
||||
And I select "Do not mark for regrade" from "menurubricregrade"
|
||||
And I press "Continue"
|
||||
And I log out
|
||||
# Students should not see anything.
|
||||
And I log in as "student1"
|
||||
And I follow "Course 1"
|
||||
And I follow "Test assignment 1 name"
|
||||
And I should not see "Criterion 1" in the ".submissionstatustable" "css_element"
|
||||
And I should not see "Criterion 2" in the ".submissionstatustable" "css_element"
|
||||
And I should not see "Criterion 3" in the ".submissionstatustable" "css_element"
|
||||
And I should not see "Rubric test description" in the ".feedback" "css_element"
|
||||
|
||||
@javascript
|
||||
Scenario: I can use rubrics to grade and edit them later updating students grades with Javascript enabled
|
||||
|
||||
Scenario: I can use rubrics to grade and edit them later updating students grades with Javascript disabled
|
||||
@@ -0,0 +1,56 @@
|
||||
@gradingform @gradingform_rubric
|
||||
Feature: Publish rubrics as templates
|
||||
In order to save time to teachers
|
||||
As a manager
|
||||
I need to publish rubrics and make them available to all teachers
|
||||
|
||||
Background:
|
||||
Given the following "users" exists:
|
||||
| username | firstname | lastname | email |
|
||||
| teacher1 | Teacher | 1 | teacher1@asd.com |
|
||||
| manager1 | Manager | 1 | manager1@asd.com |
|
||||
And the following "courses" exists:
|
||||
| fullname | shortname | format |
|
||||
| Course 1 | C1 | topics |
|
||||
And the following "activities" exists:
|
||||
| activity | course | idnumber | name | intro | advancedgradingmethod_submissions |
|
||||
| assign | C1 | A1 | Test assignment 1 name | TA1 | rubric |
|
||||
| assign | C1 | A2 | Test assignment 2 name | TA2 | rubric |
|
||||
And the following "course enrolments" exists:
|
||||
| user | course | role |
|
||||
| teacher1 | C1 | editingteacher |
|
||||
And the following "system role assigns" exists:
|
||||
| user | role | contextlevel | reference |
|
||||
| manager1 | manager | System | |
|
||||
And I log in as "manager1"
|
||||
And I follow "Course 1"
|
||||
And I go to "Test assignment 1 name" advanced grading definition page
|
||||
And I fill the moodle form with:
|
||||
| Name | Assignment 1 rubric |
|
||||
| Description | Assignment 1 description |
|
||||
And I define the following rubric:
|
||||
| Criterion 1 | Level 11 | 11 | Level 12 | 12 |
|
||||
| Criterion 2 | Level 21 | 21 | Level 22 | 22 |
|
||||
And I press "Save rubric and make it ready"
|
||||
When I publish "Test assignment 1 name" grading form definition as a public template
|
||||
And I log out
|
||||
And I log in as "teacher1"
|
||||
And I follow "Course 1"
|
||||
And I set "Test assignment 2 name" activity to use "Assignment 1 rubric" grading form
|
||||
Then I should see "Advanced grading: Test assignment 2 name (Submissions)"
|
||||
And I should see "Criterion 1"
|
||||
And I should see "Assignment 1 description"
|
||||
And I go to "Test assignment 2 name" advanced grading definition page
|
||||
And I should see "Current rubric status"
|
||||
|
||||
@javascript
|
||||
Scenario: Create a rubric template and reuse it as a teacher, with Javascript enabled
|
||||
Then the "Description" field should match "<p>Assignment 1 description</p>" value
|
||||
And I should see "Criterion 1"
|
||||
And I press "Cancel"
|
||||
|
||||
Scenario: Create a rubric template and reuse it as a teacher, with Javascript disabled
|
||||
Then the "Description" field should match "Assignment 1 description" value
|
||||
# Trying to avoid pointing by id or name as the code internals may change.
|
||||
And "//table[@class='criteria']//textarea[text()='Criterion 1']" "xpath_element" should exists
|
||||
And I press "Cancel"
|
||||
@@ -0,0 +1,52 @@
|
||||
@gradingform @gradingform_rubric
|
||||
Feature: Reuse my rubrics in other activities
|
||||
In order to save time creating duplicated grading forms
|
||||
As a teacher
|
||||
I need to reuse rubrics that I created previously
|
||||
|
||||
Background:
|
||||
Given the following "users" exists:
|
||||
| username | firstname | lastname | email |
|
||||
| teacher1 | Teacher | 1 | teacher1@asd.com |
|
||||
And the following "courses" exists:
|
||||
| fullname | shortname | format |
|
||||
| Course 1 | C1 | topics |
|
||||
And the following "course enrolments" exists:
|
||||
| user | course | role |
|
||||
| teacher1 | C1 | editingteacher |
|
||||
And I log in as "teacher1"
|
||||
And I follow "Course 1"
|
||||
And I turn editing mode on
|
||||
And I add a "Assignment" to section "1" and I fill the form with:
|
||||
| Assignment name | Test assignment 1 name |
|
||||
| Description | Test assignment 1 description |
|
||||
| Grading method | Rubric |
|
||||
And I go to "Test assignment 1 name" advanced grading definition page
|
||||
And I fill the moodle form with:
|
||||
| Name | Assignment 1 rubric |
|
||||
| Description | Assignment 1 description |
|
||||
And I define the following rubric:
|
||||
| Criterion 1 | Level 11 | 11 | Level 12 | 12 | Level 3 | 13 |
|
||||
| Criterion 2 | Level 21 | 21 | Level 22 | 22 | Level 3 | 23 |
|
||||
| Criterion 3 | Level 31 | 31 | Level 32 | 32 |
|
||||
And I press "Save rubric and make it ready"
|
||||
And I follow "Course 1"
|
||||
When I add a "Assignment" to section "1" and I fill the form with:
|
||||
| Assignment name | Test assignment 2 name |
|
||||
| Description | Test assignment 2 description |
|
||||
| Grading method | Rubric |
|
||||
And I set "Test assignment 2 name" activity to use "Assignment 1 rubric" grading form
|
||||
Then I should see "Ready for use"
|
||||
And I should see "Criterion 1"
|
||||
And I should see "Criterion 2"
|
||||
And I should see "Criterion 3"
|
||||
And I go to "Test assignment 1 name" advanced grading definition page
|
||||
And I should see "Criterion 1"
|
||||
And I should see "Criterion 2"
|
||||
And I should see "Criterion 3"
|
||||
And I press "Cancel"
|
||||
|
||||
@javascript
|
||||
Scenario: A teacher can reuse one of his/her previously created rubrics, with Javascript enabled
|
||||
|
||||
Scenario: A teacher can reuse one of his/her previously created rubrics, with Javascript disabled
|
||||
@@ -0,0 +1,181 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Grading methods steps definitions.
|
||||
*
|
||||
* @package core_grading
|
||||
* @category test
|
||||
* @copyright 2013 David Monllaó
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
// NOTE: no MOODLE_INTERNAL test here, this file may be required by behat before including /config.php.
|
||||
|
||||
require_once(__DIR__ . '/../../../../lib/behat/behat_base.php');
|
||||
|
||||
use Behat\Gherkin\Node\TableNode as TableNode,
|
||||
Behat\Behat\Context\Step\Given as Given,
|
||||
Behat\Behat\Context\Step\When as When;
|
||||
|
||||
/**
|
||||
* Generic grading methods step definitions.
|
||||
*
|
||||
* @package core_grading
|
||||
* @category test
|
||||
* @copyright 2013 David Monllaó
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class behat_grading extends behat_base {
|
||||
|
||||
/**
|
||||
* Goes to the selected advanced grading page. You should be in the course page when this step begins.
|
||||
*
|
||||
* @Given /^I go to "(?P<activity_name_string>(?:[^"]|\\")*)" advanced grading page$/
|
||||
* @param string $activityname
|
||||
* @return Given[]
|
||||
*/
|
||||
public function i_go_to_advanced_grading_page($activityname) {
|
||||
return array(
|
||||
new Given('I follow "' . $this->escape($activityname) . '"'),
|
||||
new Given('I follow "' . get_string('gradingmanagement', 'grading') . '"'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Goes to the selected advanced grading definition page. You should be in the course page when this step begins.
|
||||
*
|
||||
* @Given /^I go to "(?P<activity_name_string>(?:[^"]|\\")*)" advanced grading definition page$/
|
||||
* @param string $activityname
|
||||
* @return Given[]
|
||||
*/
|
||||
public function i_go_to_advanced_grading_definition_page($activityname) {
|
||||
|
||||
// Transforming to literals, probably not necessary, just in case.
|
||||
$newactionliteral = $this->getSession()->getSelectorsHandler()->xpathLiteral(get_string("manageactionnew", "grading"));
|
||||
$editactionliteral = $this->getSession()->getSelectorsHandler()->xpathLiteral(get_string("manageactionedit", "grading"));
|
||||
|
||||
// Working both when adding and editing.
|
||||
$definitionxpath = "//a[@class='action']" .
|
||||
"[./descendant::*[contains(., $newactionliteral) or contains(., $editactionliteral)]]";
|
||||
|
||||
return array(
|
||||
new Given('I go to "' . $this->escape($activityname) . '" advanced grading page'),
|
||||
new Given('I click on "' . $this->escape($definitionxpath) . '" "xpath_element"'),
|
||||
);
|
||||
}
|
||||
/**
|
||||
* Goes to the student's advanced grading page.
|
||||
*
|
||||
* @Given /^I go to "(?P<user_fullname_string>(?:[^"]|\\")*)" "(?P<activity_name_string>(?:[^"]|\\")*)" activity advanced grading page$/
|
||||
* @param string $userfullname The user full name including firstname and lastname.
|
||||
* @param string $activityname The activity name
|
||||
* @return Given[]
|
||||
*/
|
||||
public function i_go_to_activity_advanced_grading_page($userfullname, $activityname) {
|
||||
|
||||
// Step to access the user grade page from the grading page.
|
||||
$usergradetext = get_string('gradeuser', 'assign', $userfullname);
|
||||
$gradeuserstep = new Given('I follow "' . $this->escape($usergradetext) . '"');
|
||||
|
||||
// Shortcut in case we already are in the grading page.
|
||||
$usergradetextliteral = $this->getSession()->getSelectorsHandler()->xpathLiteral($usergradetext);
|
||||
if ($this->getSession()->getPage()->find('named', array('link', $usergradetextliteral))) {
|
||||
return $gradeuserstep;
|
||||
}
|
||||
|
||||
return array(
|
||||
new Given('I follow "' . $this->escape($activityname) . '"'),
|
||||
new Given('I follow "' . $this->escape(get_string('viewgrading', 'assign')) . '"'),
|
||||
$gradeuserstep
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Publishes current activity grading defined form as a public template.
|
||||
*
|
||||
* @Given /^I publish "(?P<activity_name_string>(?:[^"]|\\")*)" grading form definition as a public template$/
|
||||
* @param string $activityname
|
||||
* @return Given[]
|
||||
*/
|
||||
public function i_publish_grading_form_definition_as_a_public_template($activityname) {
|
||||
|
||||
return array(
|
||||
new Given('I go to "' . $this->escape($activityname) . '" advanced grading page'),
|
||||
new Given('I click on "' . $this->escape(get_string("manageactionshare", "grading")) . '" "link"'),
|
||||
new Given('I press "' . get_string('continue') . '"')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a previously created grading form as the activity grading form.
|
||||
*
|
||||
* @Given /^I set "(?P<activity_name_string>(?:[^"]|\\")*)" activity to use "(?P<grading_form_template_string>(?:[^"]|\\")*)" grading form$/
|
||||
* @param string $activityname
|
||||
* @param string $templatename
|
||||
* @return Given[]
|
||||
*/
|
||||
public function i_set_activity_to_use_grading_form($activityname, $templatename) {
|
||||
|
||||
$templateliteral = $this->getSession()->getSelectorsHandler()->xpathLiteral($templatename);
|
||||
|
||||
$templatexpath = "//h2[@class='template-name'][contains(., $templateliteral)]/" .
|
||||
"following-sibling::div[contains(concat(' ', normalize-space(@class), ' '), ' template-actions ')]";
|
||||
|
||||
// Should work with both templates and own forms.
|
||||
$literaltemplate = $this->getSession()->getSelectorsHandler()->xpathLiteral(get_string('templatepick', 'grading'));
|
||||
$literalownform = $this->getSession()->getSelectorsHandler()->xpathLiteral(get_string('templatepickownform', 'grading'));
|
||||
$usetemplatexpath = "//a[./descendant::div[text()=$literaltemplate]]|" .
|
||||
"//a[./descendant::div[text()=$literalownform]]";
|
||||
|
||||
return array(
|
||||
new Given('I go to "' . $this->escape($activityname) . '" advanced grading page'),
|
||||
new Given('I follow "' . $this->escape(get_string('manageactionclone', 'grading')) . '"'),
|
||||
new Given('I check "' . get_string('searchownforms', 'grading') . '"'),
|
||||
new Given('I click on "' . get_string('search') . '" "button" in the "region-main" "region"'),
|
||||
new Given('I click on "' . $this->escape($usetemplatexpath) . '" "xpath_element" ' .
|
||||
'in the "' . $this->escape($templatexpath) . '" "xpath_element"'),
|
||||
new Given('I press "' . get_string('continue') . '"')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the current page advanced grading form.
|
||||
*
|
||||
* @When /^I save the advanced grading form$/
|
||||
* @return When[]
|
||||
*/
|
||||
public function i_save_the_advanced_grading_form() {
|
||||
return array(
|
||||
new When('I press "' . get_string('savechanges') . '"'),
|
||||
new When('I press "' . get_string('continue') . '"')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Grades an activity using advanced grading. Note the grade is set by other steps, depending on the grading method.
|
||||
*
|
||||
* @Given /^I complete the advanced grading form with these values:$/
|
||||
* @param TableNode $data
|
||||
* @return Given[]
|
||||
*/
|
||||
public function i_complete_the_advanced_grading_form_with_these_values(TableNode $data) {
|
||||
return array(
|
||||
new Given('I fill the moodle form with:', $data),
|
||||
new Given('I save the advanced grading form')
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -235,7 +235,7 @@ if ($formdata = $mform2->get_data()) {
|
||||
$studentid = $value;
|
||||
break;
|
||||
case 'useridnumber':
|
||||
if (!$user = $DB->get_record('user', array('idnumber' => $value))) {
|
||||
if (empty($value) || !$user = $DB->get_record('user', array('idnumber' => $value))) {
|
||||
// user not found, abort whole import
|
||||
import_cleanup($importcode);
|
||||
echo $OUTPUT->notification("user mapping error, could not find user with idnumber \"$value\"");
|
||||
|
||||
@@ -179,6 +179,9 @@ table#user-grades .userpic {
|
||||
}
|
||||
table#user-grades .quickfeedback {
|
||||
border: 1px dashed #000;
|
||||
width: auto;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
margin-left: 10px;
|
||||
}
|
||||
.dir-rtl table#user-grades .quickfeedback {
|
||||
@@ -240,8 +243,9 @@ table#user-grades th.courseitem,
|
||||
border-style: solid;
|
||||
border-width: 0 1px 1px;
|
||||
}
|
||||
.path-grade-report-grader table td.topleft {
|
||||
border-bottom: 0;
|
||||
.path-grade-report-grader .left_scroller table td.topleft {
|
||||
background-color: #fff;
|
||||
border-bottom-color: #cecece;
|
||||
}
|
||||
table#user-grades td.topleft {
|
||||
background-color: #fff;
|
||||
@@ -287,6 +291,9 @@ table#user-grades td.topleft {
|
||||
}
|
||||
.path-grade-report-grader td input.text {
|
||||
border: 1px solid #666;
|
||||
width: auto;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.path-grade-report-grader td input.submit {
|
||||
margin: 10px 10px 0px 10px;
|
||||
@@ -508,6 +515,9 @@ table#user-grades td.controls,
|
||||
.path-grade-report-grader .grade_icons {
|
||||
margin-bottom: .3em;
|
||||
}
|
||||
.path-grade-report-grader tr.controls .grade_icons {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.path-grade-report-grader .yui3-overlay {
|
||||
background-color: #FFEE69;
|
||||
border-color: #D4C237 #A6982B #A6982B;
|
||||
|
||||
@@ -415,7 +415,7 @@ class grade_report_user extends grade_report {
|
||||
$data['weight']['headers'] = "$header_cat $header_row weight";
|
||||
// has a weight assigned, might be extra credit
|
||||
if ($grade_object->aggregationcoef > 0 && $type <> 'courseitem') {
|
||||
$data['weight']['content'] = number_format($grade_object->aggregationcoef,2).'%';
|
||||
$data['weight']['content'] = number_format($grade_object->aggregationcoef,2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Behat grade related steps definitions.
|
||||
*
|
||||
* @package core_grades
|
||||
* @category test
|
||||
* @copyright 2014 Mark Nelson <markn@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
// NOTE: no MOODLE_INTERNAL test here, this file may be required by behat before including /config.php.
|
||||
|
||||
require_once(__DIR__ . '/../../../lib/behat/behat_base.php');
|
||||
|
||||
use Behat\Behat\Context\Step\Given as Given;
|
||||
|
||||
class behat_grade extends behat_base {
|
||||
|
||||
/**
|
||||
* Enters a grade via the gradebook for a specific grade item and user when viewing the 'Grader report' with editing mode turned on.
|
||||
*
|
||||
* @Given /^I give the grade "(?P<grade_number>(?:[^"]|\\")*)" to the user "(?P<username_string>(?:[^"]|\\")*)" for the grade item "(?P<grade_activity_string>(?:[^"]|\\")*)"$/
|
||||
* @param int $grade
|
||||
* @param string $userfullname the user's fullname as returned by fullname()
|
||||
* @param string $itemname
|
||||
* @return Given
|
||||
*/
|
||||
public function i_give_the_grade($grade, $userfullname, $itemname) {
|
||||
$gradelabel = $userfullname . ' ' . $itemname;
|
||||
$fieldstr = get_string('useractivitygrade', 'gradereport_grader', $gradelabel);
|
||||
|
||||
return new Given('I fill in "' . $this->escape($fieldstr) . '" with "' . $grade . '"');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
@core @core_grades
|
||||
Feature: We can enter in grades and view reports from the gradebook
|
||||
In order to check the expected results are displayed
|
||||
As a teacher
|
||||
I need to assign grades and check that they display correctly in the gradebook.
|
||||
I need to enable grade weightings and check that they are displayed correctly.
|
||||
|
||||
Background:
|
||||
Given the following "courses" exists:
|
||||
| fullname | shortname | format |
|
||||
| Course 1 | C1 | topics |
|
||||
And the following "users" exists:
|
||||
| username | firstname | lastname | email |
|
||||
| teacher1 | Teacher | 1 | teacher1@asd.com |
|
||||
| student1 | Student | 1 | student1@asd.com |
|
||||
And the following "course enrolments" exists:
|
||||
| user | course | role |
|
||||
| teacher1 | C1 | editingteacher |
|
||||
| student1 | C1 | student |
|
||||
And I log in as "teacher1"
|
||||
And I follow "Course 1"
|
||||
And I turn editing mode on
|
||||
And I add a "Assignment" to section "1" and I fill the form with:
|
||||
| Assignment name | Test assignment name |
|
||||
| Description | Submit your online text |
|
||||
| assignsubmission_onlinetext_enabled | 1 |
|
||||
And I log out
|
||||
And I log in as "student1"
|
||||
And I follow "Course 1"
|
||||
And I follow "Test assignment name"
|
||||
When I press "Add submission"
|
||||
And I fill the moodle form with:
|
||||
| Online text | This is a submission |
|
||||
And I press "Save changes"
|
||||
Then I should see "Submitted for grading"
|
||||
And I log out
|
||||
And I log in as "teacher1"
|
||||
And I follow "Course 1"
|
||||
And I follow "Grades"
|
||||
And I turn editing mode on
|
||||
And I give the grade "80.00" to the user "Student 1" for the grade item "Test assignment name"
|
||||
And I press "Update"
|
||||
|
||||
@javascript
|
||||
Scenario: Grade a grade item and ensure the results display correctly in the gradebook
|
||||
When I select "User report" from "Grade report"
|
||||
And the "Grade report" select box should contain "Grader report"
|
||||
And the "Grade report" select box should contain "Outcomes report"
|
||||
And the "Grade report" select box should contain "User report"
|
||||
And the "Select all or one user" select box should contain "All users (1)"
|
||||
And I log out
|
||||
And I log in as "student1"
|
||||
And I follow "Course 1"
|
||||
And I follow "Grades"
|
||||
And I should see "80.00" in the "Test assignment name" "table_row"
|
||||
And I select "Overview report" from "Grade report"
|
||||
And I should see "80.00" in the "overview-grade" "table"
|
||||
|
||||
@javascript
|
||||
Scenario: We can add a weighting to a grade item and it is displayed properly in the user report
|
||||
When I select "Full view" from "Grade report"
|
||||
And I select "Weighted mean of grades" from "Aggregation"
|
||||
And I fill the moodle form with:
|
||||
| Extra credit value for Test assignment name | 0.72 |
|
||||
And I press "Save changes"
|
||||
And I select "User report" from "Grade report"
|
||||
And I follow "Course grade settings"
|
||||
And I fill the moodle form with:
|
||||
| Show weightings | Show |
|
||||
And I press "Save changes"
|
||||
And I log out
|
||||
And I log in as "student1"
|
||||
And I follow "Course 1"
|
||||
And I follow "Grades"
|
||||
Then I should see "0.72" in the "Test assignment name" "table_row"
|
||||
And I should not see "0.72%" in the "Test assignment name" "table_row"
|
||||
@@ -52,7 +52,7 @@ class core_grade_edittreelib_testcase extends advanced_testcase {
|
||||
$scale = $this->getDataGenerator()->create_scale();
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$assign = $this->getDataGenerator()->create_module('assign', array('course' => $course->id));
|
||||
$modulecontext = context_module::instance($assign->id);
|
||||
$modulecontext = context_module::instance($assign->cmid);
|
||||
// The generator returns a dummy object, lets get the real assign object.
|
||||
$assign = new assign($modulecontext, false, false);
|
||||
$cm = $assign->get_course_module();
|
||||
|
||||
@@ -66,7 +66,7 @@ class core_grading_externallib_testcase extends externallib_advanced_testcase {
|
||||
// Create a teacher and give them capabilities.
|
||||
$coursecontext = context_course::instance($course->id);
|
||||
$roleid = $this->assignUserCapability('moodle/course:viewparticipants', $coursecontext->id, 3);
|
||||
$modulecontext = context_module::instance($cm->id);
|
||||
$modulecontext = context_module::instance($cm->cmid);
|
||||
$this->assignUserCapability('mod/assign:grade', $modulecontext->id, $roleid);
|
||||
|
||||
// Create the teacher's enrolment record.
|
||||
@@ -146,7 +146,7 @@ class core_grading_externallib_testcase extends externallib_advanced_testcase {
|
||||
$DB->insert_record('gradingform_rubric_levels', $rubriclevel2);
|
||||
|
||||
// Call the external function.
|
||||
$cmids = array ($cm->id);
|
||||
$cmids = array ($cm->cmid);
|
||||
$areaname = 'submissions';
|
||||
$result = core_grading_external::get_definitions($cmids, $areaname);
|
||||
|
||||
|
||||
+5
-3
@@ -50,6 +50,11 @@ $strgroups = get_string('groups');
|
||||
$strparticipants = get_string('participants');
|
||||
$strautocreategroups = get_string('autocreategroups', 'group');
|
||||
|
||||
$PAGE->set_title($strgroups);
|
||||
$PAGE->set_heading($course->fullname. ': '.$strgroups);
|
||||
$PAGE->set_pagelayout('standard');
|
||||
navigation_node::override_active_url(new moodle_url('/group/index.php', array('id' => $courseid)));
|
||||
|
||||
// Print the page and form
|
||||
$preview = '';
|
||||
$error = '';
|
||||
@@ -233,9 +238,6 @@ $PAGE->navbar->add($strparticipants, new moodle_url('/user/index.php', array('id
|
||||
$PAGE->navbar->add($strgroups, new moodle_url('/group/index.php', array('id'=>$courseid)));
|
||||
$PAGE->navbar->add($strautocreategroups);
|
||||
|
||||
/// Print header
|
||||
$PAGE->set_title($strgroups);
|
||||
$PAGE->set_heading($course->fullname. ': '.$strgroups);
|
||||
echo $OUTPUT->header();
|
||||
echo $OUTPUT->heading($strautocreategroups);
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ $groupids = required_param('groups', PARAM_SEQUENCE);
|
||||
$confirm = optional_param('confirm', 0, PARAM_BOOL);
|
||||
|
||||
$PAGE->set_url('/group/delete.php', array('courseid'=>$courseid,'groups'=>$groupids));
|
||||
$PAGE->set_pagelayout('standard');
|
||||
|
||||
// Make sure course is OK and user has access to manage groups
|
||||
if (!$course = $DB->get_record('course', array('id' => $courseid))) {
|
||||
|
||||
+6
-2
@@ -74,6 +74,12 @@ require_login($course);
|
||||
$context = context_course::instance($course->id);
|
||||
require_capability('moodle/course:managegroups', $context);
|
||||
|
||||
$strgroups = get_string('groups');
|
||||
$PAGE->set_title($strgroups);
|
||||
$PAGE->set_heading($course->fullname . ': '.$strgroups);
|
||||
$PAGE->set_pagelayout('standard');
|
||||
navigation_node::override_active_url(new moodle_url('/group/index.php', array('id' => $course->id)));
|
||||
|
||||
$returnurl = $CFG->wwwroot.'/group/index.php?id='.$course->id.'&group='.$id;
|
||||
|
||||
// Prepare the description editor: We do support files for group descriptions
|
||||
@@ -123,8 +129,6 @@ $PAGE->navbar->add($strgroups, new moodle_url('/group/index.php', array('id'=>$c
|
||||
$PAGE->navbar->add($strheading);
|
||||
|
||||
/// Print header
|
||||
$PAGE->set_title($strgroups);
|
||||
$PAGE->set_heading($course->fullname . ': '.$strgroups);
|
||||
echo $OUTPUT->header();
|
||||
echo '<div id="grouppicture">';
|
||||
if ($id) {
|
||||
|
||||
+6
-5
@@ -66,8 +66,13 @@ require_login($course);
|
||||
$context = context_course::instance($course->id);
|
||||
require_capability('moodle/course:managegroups', $context);
|
||||
|
||||
$returnurl = $CFG->wwwroot.'/group/groupings.php?id='.$course->id;
|
||||
$strgroupings = get_string('groupings', 'group');
|
||||
$PAGE->set_title($strgroupings);
|
||||
$PAGE->set_heading($course->fullname. ': '.$strgroupings);
|
||||
$PAGE->set_pagelayout('standard');
|
||||
navigation_node::override_active_url(new moodle_url('/group/index.php', array('id' => $course->id)));
|
||||
|
||||
$returnurl = $CFG->wwwroot.'/group/groupings.php?id='.$course->id;
|
||||
|
||||
if ($id and $delete) {
|
||||
if (!empty($grouping->idnumber) && !has_capability('moodle/course:changeidnumber', $context)) {
|
||||
@@ -126,9 +131,7 @@ if ($editform->is_cancelled()) {
|
||||
|
||||
}
|
||||
|
||||
$strgroupings = get_string('groupings', 'group');
|
||||
$strparticipants = get_string('participants');
|
||||
|
||||
if ($id) {
|
||||
$strheading = get_string('editgroupingsettings', 'group');
|
||||
} else {
|
||||
@@ -140,8 +143,6 @@ $PAGE->navbar->add($strgroupings, new moodle_url('/group/groupings.php', array('
|
||||
$PAGE->navbar->add($strheading);
|
||||
|
||||
/// Print header
|
||||
$PAGE->set_title($strgroupings);
|
||||
$PAGE->set_heading($course->fullname. ': '.$strgroupings);
|
||||
echo $OUTPUT->header();
|
||||
echo $OUTPUT->heading($strheading);
|
||||
$editform->display();
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user