Skip to content
Browse files

Merge branch 'master' into install_master

  • Loading branch information...
2 parents 6ff2ebd + bdd045c commit 7081e477f243c2d8e1958d78123d9a755d88957b AMOS bot committed
Showing with 9,533 additions and 5,404 deletions.
  1. +4 −2 admin/cli/install.php
  2. +3 −0 admin/environment.xml
  3. +2 −1 admin/settings/server.php
  4. +7 −3 admin/tests/behat/behat_admin.php
  5. +1 −1 admin/tests/behat/display_short_names.feature
  6. +4 −0 admin/tool/behat/cli/util.php
  7. +1 −2 admin/tool/behat/lang/en/tool_behat.php
  8. +2 −2 admin/tool/behat/tests/behat/basic_actions.feature
  9. +51 −8 admin/tool/behat/tests/behat/data_generators.feature
  10. +1 −0 admin/tool/customlang/locallib.php
  11. +4 −6 admin/tool/profiling/index.php
  12. +1 −1 admin/tool/profiling/lang/en/tool_profiling.php
  13. +0 −3 admin/tool/uploaduser/index.php
  14. +3 −2 admin/tool/uploaduser/user_form.php
  15. +110 −0 auth/classes/event/user_loggedin.php
  16. +1 −0 auth/db/tests/db_test.php
  17. +75 −0 auth/tests/auth_test.php
  18. +5 −5 auth/tests/behat/behat_auth.php
  19. +19 −4 backup/backupfilesedit_form.php
  20. +1 −1 backup/cc/cc112moodle.php
  21. +1 −1 backup/cc/cc2moodle.php
  22. +6 −2 backup/cc/cc_lib/cc_utils.php
  23. +2 −1 backup/cc/cc_lib/gral_lib/pathutils.php
  24. +1 −1 backup/cc/entities.class.php
  25. +1 −1 backup/cc/entities11.class.php
  26. +1 −1 backup/converter/imscc11/backuplib.php
  27. +13 −6 backup/util/dbops/restore_dbops.class.php
  28. +1 −1 backup/util/helper/backup_helper.class.php
  29. +2 −2 backup/util/ui/tests/behat/backup_courses.feature
  30. +46 −33 backup/util/ui/tests/behat/behat_backup.php
  31. +42 −5 backup/util/xml/parser/processors/grouped_parser_processor.class.php
  32. +14 −1 badges/backpack.js
  33. +2 −4 badges/badge.php
  34. +2 −5 badges/mybadges.php
  35. +7 −4 badges/renderer.php
  36. +7 −4 blocks/comments/tests/behat/behat_block_comments.php
  37. +3 −3 blocks/course_overview/renderer.php
  38. +6 −2 blocks/recent_activity/block_recent_activity.php
  39. +2 −2 blocks/tests/behat/behat_blocks.php
  40. +1 −1 blocks/tests/behat/configure_block_throughout_site.feature
  41. +1 −0 cache/locallib.php
  42. +1 −0 cache/stores/file/lib.php
  43. +2 −1 cache/stores/memcache/addinstanceform.php
  44. +1 −0 cache/stores/memcache/lang/en/cachestore_memcache.php
  45. +1 −1 cache/stores/memcache/lib.php
  46. +32 −1 cache/stores/memcache/tests/memcache_test.php
  47. +4 −3 cache/stores/memcached/addinstanceform.php
  48. +1 −0 cache/stores/memcached/lang/en/cachestore_memcached.php
  49. +2 −2 cache/stores/memcached/lib.php
  50. +32 −1 cache/stores/memcached/tests/memcached_test.php
  51. +69 −3 cache/stores/session/lib.php
  52. +72 −0 cache/stores/session/tests/session_test.php
  53. +70 −3 cache/stores/static/lib.php
  54. +74 −1 cache/stores/static/tests/static_test.php
  55. +2 −2 cache/tests/fixtures/stores.php
  56. +2 −2 cohort/externallib.php
  57. +9 −9 cohort/tests/behat/behat_cohort.php
  58. +2 −2 cohort/tests/behat/upload_cohort_users.feature
  59. +30 −4 cohort/tests/externallib_test.php
  60. +9 −0 cohort/upgrade.txt
  61. +12 −9 completion/tests/behat/behat_completion.php
  62. +12 −4 config-dist.php
  63. +7 −4 course/edit_form.php
  64. +31 −0 course/format/singleactivity/format.php
  65. +34 −0 course/format/singleactivity/lang/en/format_singleactivity.php
  66. +443 −0 course/format/singleactivity/lib.php
  67. +63 −0 course/format/singleactivity/renderer.php
  68. +33 −0 course/format/singleactivity/settings.php
  69. +52 −0 course/format/singleactivity/settingslib.php
  70. +10 −0 course/format/singleactivity/styles.css
  71. +29 −0 course/format/singleactivity/version.php
  72. +2 −3 course/renderer.php
  73. +6 −6 course/tests/behat/activities_group_icons.feature
  74. +2 −2 course/tests/behat/activities_indentation.feature
  75. +46 −42 course/tests/behat/behat_course.php
  76. +15 −15 course/tests/behat/force_group_mode.feature
  77. +153 −0 enrol/category/classes/observer.php
  78. +9 −13 enrol/category/db/events.php
  79. +0 −125 enrol/category/locallib.php
  80. +7 −7 enrol/category/tests/{sync_test.php → plugin_test.php}
  81. +1 −0 enrol/database/tests/sync_test.php
  82. +1 −1 enrol/externallib.php
  83. +51 −43 enrol/guest/lib.php
  84. +4 −4 enrol/tests/behat/behat_enrol.php
  85. +3 −0 enrol/upgrade.txt
  86. +1 −0 files/externallib.php
  87. +7 −4 grade/grading/form/guide/lib.php
  88. +2 −2 grade/grading/form/guide/version.php
  89. +31 −5 grade/grading/form/lib.php
  90. +6 −3 grade/grading/form/rubric/lib.php
  91. +2 −2 grade/grading/form/rubric/styles.css
  92. +2 −2 grade/grading/form/rubric/version.php
  93. +9 −0 grade/grading/form/upgrade.txt
  94. +9 −5 grade/import/lib.php
  95. +10 −13 grade/lib.php
  96. +29 −37 grade/report/grader/lib.php
  97. +8 −5 grade/report/lib.php
  98. +10 −21 grade/report/user/lib.php
  99. +11 −8 group/lib.php
  100. +9 −6 group/tests/behat/behat_groups.php
  101. +2 −0 install.php
  102. +0 −1 lang/en/admin.php
  103. +2 −0 lang/en/auth.php
  104. +1 −0 lang/en/badges.php
  105. +1 −0 lang/en/cache.php
  106. +9 −116 lang/en/install.php
  107. +72 −22 lib/accesslib.php
  108. +1 −0 lib/adminlib.php
  109. +5 −0 lib/authlib.php
  110. +16 −0 lib/badgeslib.php
  111. +25 −17 lib/behat/behat_files.php
  112. +4 −1 lib/behat/form_field/behat_form_select.php
  113. +32 −0 lib/behat/lib.php
  114. +1 −1 lib/classes/component.php
  115. +617 −0 lib/classes/event/base.php
  116. +349 −0 lib/classes/event/manager.php
  117. +80 −0 lib/classes/event/role_assigned.php
  118. +80 −0 lib/classes/event/role_unassigned.php
  119. +5 −2 lib/datalib.php
  120. +9 −0 lib/db/caches.php
  121. +15 −14 lib/db/events.php
  122. +19 −4 lib/deprecatedlib.php
  123. +110 −0 lib/dml/mariadb_native_moodle_database.php
  124. +19 −1 lib/dml/moodle_database.php
  125. +0 −15 lib/dml/mssql_native_moodle_database.php
  126. +0 −9 lib/dml/mysqli_native_moodle_database.php
  127. +0 −9 lib/dml/oci_native_moodle_database.php
  128. +0 −9 lib/dml/pdo_moodle_database.php
  129. +0 −9 lib/dml/pgsql_native_moodle_database.php
  130. +3 −1 lib/dml/sqlite3_pdo_moodle_database.php
  131. +0 −15 lib/dml/sqlsrv_native_moodle_database.php
  132. +0 −1 lib/dml/tests/dml_test.php
  133. +2 −0 lib/dtl/file_xml_database_exporter.php
  134. +61 −28 lib/editor/tinymce/classes/plugin.php
  135. +7 −2 lib/editor/tinymce/plugins/dragmath/lib.php
  136. +33 −0 lib/editor/tinymce/plugins/managefiles/lang/en/tinymce_managefiles.php
  137. +74 −0 lib/editor/tinymce/plugins/managefiles/lib.php
  138. +97 −0 lib/editor/tinymce/plugins/managefiles/manage.php
  139. +92 −0 lib/editor/tinymce/plugins/managefiles/manage_form.php
  140. +30 −0 lib/editor/tinymce/plugins/managefiles/module.js
  141. BIN lib/editor/tinymce/plugins/managefiles/pix/icon.gif
  142. +7 −0 lib/editor/tinymce/plugins/managefiles/styles.css
  143. +132 −0 lib/editor/tinymce/plugins/managefiles/tinymce/editor_plugin.js
  144. BIN lib/editor/tinymce/plugins/managefiles/tinymce/img/managefiles.png
  145. +29 −0 lib/editor/tinymce/plugins/managefiles/version.php
  146. +7 −2 lib/editor/tinymce/plugins/moodleemoticon/lib.php
  147. +11 −8 lib/editor/tinymce/plugins/moodlemedia/lib.php
  148. +7 −2 lib/editor/tinymce/plugins/moodlenolink/lib.php
  149. +6 −14 lib/editor/tinymce/plugins/pdw/lib.php
  150. +4 −11 lib/editor/tinymce/plugins/spellchecker/config.php
  151. +10 −2 lib/editor/tinymce/plugins/spellchecker/lib.php
  152. +2 −2 lib/editor/tinymce/plugins/spellchecker/settings.php
  153. +122 −0 lib/editor/tinymce/tests/editor_test.php
  154. +3 −0 lib/editor/tinymce/upgrade.txt
  155. +1 −1 lib/environmentlib.php
  156. +2 −0 lib/filelib.php
  157. +3 −0 lib/filestorage/file_storage.php
  158. +186 −72 lib/form/form.js
  159. +1 −1 lib/installlib.php
  160. +1 −1 lib/javascript.php
  161. +4,394 −4,202 lib/jquery/{jquery-1.9.1.js → jquery-1.10.2.js}
  162. +6 −0 lib/jquery/jquery-1.10.2.min.js
  163. +0 −5 lib/jquery/jquery-1.9.1.min.js
  164. +0 −3 lib/jquery/jquery-migrate-1.1.1.min.js
  165. +18 −8 lib/jquery/{jquery-migrate-1.1.1.js → jquery-migrate-1.2.1.js}
  166. +2 −0 lib/jquery/jquery-migrate-1.2.1.min.js
  167. +4 −4 lib/jquery/plugins.php
  168. +0 −4 lib/jquery/ui-1.10.2/css/base/jquery-ui.min.css
  169. +0 −12 lib/jquery/ui-1.10.2/jquery-ui.min.js
  170. 0 lib/jquery/{ui-1.10.2 → ui-1.10.3}/css/base/images/animated-overlay.gif
  171. 0 lib/jquery/{ui-1.10.2 → ui-1.10.3}/css/base/images/ui-bg_flat_0_aaaaaa_40x100.png
  172. 0 lib/jquery/{ui-1.10.2 → ui-1.10.3}/css/base/images/ui-bg_flat_75_ffffff_40x100.png
  173. 0 lib/jquery/{ui-1.10.2 → ui-1.10.3}/css/base/images/ui-bg_glass_55_fbf9ee_1x400.png
  174. 0 lib/jquery/{ui-1.10.2 → ui-1.10.3}/css/base/images/ui-bg_glass_65_ffffff_1x400.png
  175. 0 lib/jquery/{ui-1.10.2 → ui-1.10.3}/css/base/images/ui-bg_glass_75_dadada_1x400.png
  176. 0 lib/jquery/{ui-1.10.2 → ui-1.10.3}/css/base/images/ui-bg_glass_75_e6e6e6_1x400.png
  177. 0 lib/jquery/{ui-1.10.2 → ui-1.10.3}/css/base/images/ui-bg_glass_95_fef1ec_1x400.png
  178. 0 lib/jquery/{ui-1.10.2 → ui-1.10.3}/css/base/images/ui-bg_highlight-soft_75_cccccc_1x100.png
  179. 0 lib/jquery/{ui-1.10.2 → ui-1.10.3}/css/base/images/ui-icons_222222_256x240.png
  180. 0 lib/jquery/{ui-1.10.2 → ui-1.10.3}/css/base/images/ui-icons_2e83ff_256x240.png
  181. 0 lib/jquery/{ui-1.10.2 → ui-1.10.3}/css/base/images/ui-icons_454545_256x240.png
  182. 0 lib/jquery/{ui-1.10.2 → ui-1.10.3}/css/base/images/ui-icons_888888_256x240.png
  183. 0 lib/jquery/{ui-1.10.2 → ui-1.10.3}/css/base/images/ui-icons_cd0a0a_256x240.png
  184. +3 −1 lib/jquery/{ui-1.10.2 → ui-1.10.3}/css/base/jquery-ui.css
  185. +4 −0 lib/jquery/ui-1.10.3/css/base/jquery-ui.min.css
  186. +138 −122 lib/jquery/{ui-1.10.2 → ui-1.10.3}/jquery-ui.js
  187. +12 −0 lib/jquery/ui-1.10.3/jquery-ui.min.js
  188. +7 −0 lib/moodlelib.php
  189. +2 −1 lib/navigationlib.php
  190. +117 −3 lib/outputlib.php
  191. +0 −2 lib/outputrequirementslib.php
  192. +92 −84 lib/pagelib.php
  193. +13 −0 lib/phpunit/classes/advanced_testcase.php
  194. +87 −0 lib/phpunit/classes/event_sink.php
  195. +60 −0 lib/phpunit/classes/util.php
  196. +1 −0 lib/phpunit/lib.php
  197. +5 −4 lib/pluginlib.php
  198. +24 −4 lib/setup.php
  199. +64 −5 lib/setuplib.php
  200. +1 −0 lib/tablelib.php
  201. +1 −0 lib/testing/classes/util.php
  202. +28 −0 lib/testing/generator/data_generator.php
Sorry, we could not display the entire diff because too many files (316) changed.
View
6 admin/cli/install.php
@@ -188,6 +188,7 @@
//Database types
$databases = array('mysqli' => moodle_database::get_driver_instance('mysqli', 'native'),
+ 'mariadb'=> moodle_database::get_driver_instance('mariadb', 'native'),
'pgsql' => moodle_database::get_driver_instance('pgsql', 'native'),
'oci' => moodle_database::get_driver_instance('oci', 'native'),
'sqlsrv' => moodle_database::get_driver_instance('sqlsrv', 'native'), // MS SQL*Server PHP driver
@@ -394,8 +395,9 @@
cli_error(get_string('pathserrcreatedataroot', 'install', $a));
}
}
-$CFG->tempdir = $CFG->dataroot.'/temp';
-$CFG->cachedir = $CFG->dataroot.'/cache';
+$CFG->tempdir = $CFG->dataroot.'/temp';
+$CFG->cachedir = $CFG->dataroot.'/cache';
+$CFG->localcachedir = $CFG->dataroot.'/localcache';
// download required lang packs
if ($CFG->lang !== 'en') {
View
3 admin/environment.xml
@@ -899,6 +899,7 @@
</FEEDBACK>
</UNICODE>
<DATABASE level="required">
+ <VENDOR name="mariadb" version="5.3.5" />
<VENDOR name="mysql" version="5.1.33" />
<VENDOR name="postgres" version="8.3" />
<VENDOR name="mssql" version="9.0" />
@@ -957,6 +958,8 @@
<ON_ERROR message="ziprequired" />
</FEEDBACK>
</PHP_EXTENSION>
+ <PHP_EXTENSION name="zlib" level="optional">
+ </PHP_EXTENSION>
<PHP_EXTENSION name="gd" level="required">
<FEEDBACK>
<ON_ERROR message="gdrequired" />
View
3 admin/settings/server.php
@@ -182,7 +182,8 @@
'128M' => '128M',
'256M' => '256M',
'512M' => '512M',
- '1024M' => '1024M'
+ '1024M' => '1024M',
+ '2048M' => '2048M',
)));
$temp->add(new admin_setting_configtext('curlcache', new lang_string('curlcache', 'admin'),
new lang_string('configcurlcache', 'admin'), 120, PARAM_INT));
View
10 admin/tests/behat/behat_admin.php
@@ -62,7 +62,7 @@ public function i_set_the_following_administration_settings_values(TableNode $ta
}
// Search by label.
- $searchbox = $this->find_field('Search in settings');
+ $searchbox = $this->find_field(get_string('searchinsettings', 'admin'));
$searchbox->setValue($label);
$submitsearch = $this->find('css', 'form.adminsearchform input[type=submit]');
$submitsearch->press();
@@ -72,8 +72,12 @@ public function i_set_the_following_administration_settings_values(TableNode $ta
// Admin settings does not use the same DOM structure than other moodle forms
// but we also need to use lib/behat/form_field/* to deal with the different moodle form elements.
$exception = new ElementNotFoundException($this->getSession(), '"' . $label . '" administration setting ');
+
+ // The argument should be converted to an xpath literal.
+ $label = $this->getSession()->getSelectorsHandler()->xpathLiteral($label);
+
$fieldxpath = "//*[self::input | self::textarea | self::select][not(./@type = 'submit' or ./@type = 'image' or ./@type = 'hidden')]" .
- "[@id=//label[contains(normalize-space(string(.)), '" . $label . "')]/@for]";
+ "[@id=//label[contains(normalize-space(.), $label)]/@for]";
$fieldnode = $this->find('xpath', $fieldxpath, $exception);
$formfieldtypenode = $this->find('xpath', $fieldxpath . "/ancestor::div[@class='form-setting']" .
"/child::div[contains(concat(' ', @class, ' '), ' form-')]/child::*/parent::div");
@@ -90,7 +94,7 @@ public function i_set_the_following_administration_settings_values(TableNode $ta
$field = behat_field_manager::get_field_instance($type, $fieldnode, $this->getSession());
$field->set_value($value);
- $this->find_button('Save changes')->press();
+ $this->find_button(get_string('savechanges'))->press();
}
}
View
2 admin/tests/behat/display_short_names.feature
@@ -15,7 +15,7 @@ Feature: Display extended course names
And I should not see "C_shortname Course fullname"
Scenario: Courses list with extended course names
- Given I click on "Courses" "link" in the "//div[@id='settingsnav']//descendant::li[contains(concat(' ', @class, ' '), ' type_setting ')][not(contains(., 'Site administration'))][contains(., 'Appearance')]" "xpath_element"
+ Given I click on "Courses" "link" in the "//div[@id='settingsnav']/descendant::li[contains(concat(' ', normalize-space(@class), ' '), ' type_setting ')][not(contains(., 'Site administration'))][contains(., 'Appearance')]" "xpath_element"
And I check "Display extended course names"
When I press "Save changes"
And I am on homepage
View
4 admin/tool/behat/cli/util.php
@@ -143,6 +143,9 @@
$CFG->{$var} = $CFG->{'behat_' . $var};
}
+// Clean $CFG extra values before performing any action.
+behat_clean_init_config();
+
$CFG->noemailever = true;
$CFG->passwordsaltmain = 'moodle';
@@ -151,6 +154,7 @@
// Unset cache and temp directories to reset them again with the new $CFG->dataroot.
unset($CFG->cachedir);
+unset($CFG->localcachedir);
unset($CFG->tempdir);
// Continues setup.
View
3 admin/tool/behat/lang/en/tool_behat.php
@@ -41,6 +41,5 @@
$string['viewsteps'] = 'Filter';
$string['wheninfo'] = 'When. Actions that provokes an event';
$string['wrongbehatsetup'] = 'Something is wrong with behat setup, ensure:<ul>
-<li>You ran "curl http://getcomposer.org/installer | php"</li>
-<li>You ran "php composer.phar install --dev"</li>
+<li>You ran "php admin/tool/behat/cli/init.php" from your moodle root directory</li>
<li>vendor/bin/behat file has execution permissions</li></ul>';
View
4 admin/tool/behat/tests/behat/basic_actions.feature
@@ -37,7 +37,7 @@ Feature: Page contents assertions
And I follow "Course 1"
When I click on "Move this to the dock" "button" in the ".block_settings" "css_element"
Then I should not see "Question bank"
- And I click on "//div[@id='dock']/descendant::*[contains(., 'Administration')]/h2" "xpath_element"
+ And I click on "//div[@id='dock']/descendant::h2[normalize-space(.)='Administration']" "xpath_element"
@javascript
Scenario: Locators inside specific DOM nodes using XPath
@@ -45,5 +45,5 @@ Feature: Page contents assertions
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And I log in as "admin"
- When I click on "Move this to the dock" "button" in the "//*[contains(concat(' ', normalize-space(@class), ' '), ' block_settings ')]" "xpath_element"
+ When I click on "Move this to the dock" "button" in the "//div[contains(concat(' ', normalize-space(@class), ' '), ' block_settings ')]" "xpath_element"
Then I should not see "Turn editing on"
View
59 admin/tool/behat/tests/behat/data_generators.feature
@@ -30,17 +30,17 @@ Feature: Set up contextual data for tests
Then I should see "Course 1"
And I should see "Course 2"
And I should see "Course 3"
- When I go to the courses management page
+ And I go to the courses management page
And I follow "Cat 1"
- Then I should see "Cat 2"
+ And I should see "Cat 2"
And I should see "Cat 3"
- When I follow "Cat 3"
- Then I should see "Course 1"
+ And I follow "Cat 3"
+ And I should see "Course 1"
And I should see "Course 2"
- When I select "Cat 2" from "Course categories:"
- Then I should see "No courses in this category"
- When I select "Miscellaneous" from "Course categories:"
- Then I should see "Course 3"
+ And I select "Cat 1 / Cat 2" from "Course categories:"
+ And I should see "No courses in this category"
+ And I select "Miscellaneous" from "Course categories:"
+ And I should see "Course 3"
@javascript
Scenario: Add a bunch of groups and groupings
@@ -79,6 +79,49 @@ Feature: Set up contextual data for tests
And I follow "Course 1"
Then I should see "Topic 1"
+ Scenario: Add role assigns
+ Given the following "users" exists:
+ | username | firstname | lastname | email |
+ | user1 | User | 1 | user1@moodlemoodle.com |
+ | user2 | User | 2 | user2@moodlemoodle.com |
+ | user3 | User | 3 | user3@moodlemoodle.com |
+ And the following "categories" exists:
+ | name | category | idnumber |
+ | Cat 1 | 0 | CAT1 |
+ And the following "courses" exists:
+ | fullname | shortname | category |
+ | Course 1 | C1 | CAT1 |
+ And the following "role assigns" exists:
+ | user | role | contextlevel | reference |
+ | user1 | manager | System | |
+ | user2 | editingteacher | Category | CAT1 |
+ | user3 | editingteacher | Course | C1 |
+ When I log in as "user1"
+ Then I should see "Front page settings"
+ And I log out
+ And I log in as "user2"
+ And I follow "Course 1"
+ And I should see "Turn editing on"
+ And I log out
+ And I log in as "user3"
+ And I follow "Course 1"
+ And I should see "Turn editing on"
+
+ Scenario: Add modules
+ Given the following "courses" exists:
+ | fullname | shortname |
+ | Course 1 | C1 |
+ And the following "activities" exists:
+ | activity | name | intro | course | idnumber |
+ | assign | Test assignment name | Test assignment description | C1 | assign1 |
+ | data | Test database name | Test database description | C1 | data1 |
+ When I log in as "admin"
+ And I follow "Course 1"
+ Then I should see "Test assignment name"
+ And I should see "Test database name"
+ And I follow "Test assignment name"
+ And I should see "Test assignment description"
+
@javascript
Scenario: Add relations between users and groups
Given the following "users" exists:
View
1 admin/tool/customlang/locallib.php
@@ -323,6 +323,7 @@ protected static function dump_strings($lang, $component, $strings) {
fwrite($f, ";\n");
}
fclose($f);
+ @chmod($filepath, $CFG->filepermissions);
}
/**
View
10 admin/tool/profiling/index.php
@@ -74,12 +74,11 @@
$prevreferences = $DB->get_records_select('profiling',
'url = ? AND runreference = 1 AND timecreated < ?',
array($run->url, $run->timecreated),
- 'timecreated DESC', 'runid', 0, 1);
- $prevrunid = $prevreferences ? reset($prevreferences)->runid : false;
+ 'timecreated DESC', 'runid, runcomment, timecreated', 0, 10);
echo $OUTPUT->box_start('generalbox boxwidthwide boxaligncenter');
$header = get_string('lastrunof', 'tool_profiling', $script);
echo $OUTPUT->heading($header);
- $table = profiling_print_run($run, $prevrunid);
+ $table = profiling_print_run($run, $prevreferences);
echo $table;
echo $OUTPUT->box_end();
@@ -126,12 +125,11 @@
$prevreferences = $DB->get_records_select('profiling',
'url = ? AND runreference = 1 AND timecreated < ?',
array($run->url, $run->timecreated),
- 'timecreated DESC', 'runid', 0, 1);
- $prevrunid = $prevreferences ? reset($prevreferences)->runid : false;
+ 'timecreated DESC', 'runid, runcomment, timecreated', 0, 10);
echo $OUTPUT->box_start('generalbox boxwidthwide boxaligncenter');
$header = get_string('summaryof', 'tool_profiling', $run->url);
echo $OUTPUT->heading($header);
- $table = profiling_print_run($run, $prevrunid);
+ $table = profiling_print_run($run, $prevreferences);
echo $table;
echo $OUTPUT->box_end();
View
2 admin/tool/profiling/lang/en/tool_profiling.php
@@ -51,5 +51,5 @@
$string['runid'] = 'Run ID';
$string['summaryof'] = 'Summary of {$a}';
$string['viewdetails'] = 'View profiling details';
-$string['viewdiff'] = 'View profiling differences with last reference run';
+$string['viewdiff'] = 'View profiling differences with:';
$string['viewdiffdetails'] = 'View profiling diff details';
View
3 admin/tool/uploaduser/index.php
@@ -1110,9 +1110,6 @@
if (isset($rowcols['city'])) {
$rowcols['city'] = trim($rowcols['city']);
- if (empty($rowcols['city'])) {
- $rowcols['status'][] = get_string('fieldrequired', 'error', 'city');
- }
}
// Check if rowcols have custom profile field with correct data and update error state.
$noerror = uu_check_custom_profile_data($rowcols) && $noerror;
View
5 admin/tool/uploaduser/user_form.php
@@ -257,9 +257,10 @@ function definition () {
} else {
$mform->setDefault('city', $CFG->defaultcity);
}
- $mform->addRule('city', get_string('required'), 'required');
- $mform->addElement('select', 'country', get_string('selectacountry'), get_string_manager()->get_list_of_countries());
+ $choices = get_string_manager()->get_list_of_countries();
+ $choices = array(''=>get_string('selectacountry').'...') + $choices;
+ $mform->addElement('select', 'country', get_string('selectacountry'), $choices);
if (empty($CFG->country)) {
$mform->setDefault('country', $templateuser->country);
} else {
View
110 auth/classes/event/user_loggedin.php
@@ -0,0 +1,110 @@
+<?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/>.
+
+/**
+ * User login event.
+ *
+ * @package core_auth
+ * @copyright 2013 Frédéric Massart
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace core_auth\event;
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * User login event class.
+ *
+ * @package core_auth
+ * @copyright 2013 Frédéric Massart
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class user_loggedin extends \core\event\base {
+
+ /**
+ * Returns localised description of what happened.
+ *
+ * @return \lang_string.
+ */
+ public function get_description() {
+ return new \lang_string('event_user_loggedin_desc', '', $this->get_username());
+ }
+
+ /**
+ * Return legacy data for add_to_log().
+ *
+ * @return array
+ */
+ public function get_legacy_logdata() {
+ return array(SITEID, 'user', 'login', "view.php?id=" . $this->data['objectid'] . "&course=".SITEID,
+ $this->data['objectid'], 0, $this->data['objectid']);
+ }
+
+ /**
+ * Return localised event name.
+ *
+ * @return \lang_string
+ */
+ public static function get_name() {
+ return new \lang_string('event_user_loggedin');
+ }
+
+ /**
+ * Get URL related to the action
+ *
+ * @return \moodle_url
+ */
+ public function get_url() {
+ return new \moodle_url('/user/profile.php', array('id' => $this->data['objectid']));
+ }
+
+ /**
+ * Return the username of the logged in user.
+ *
+ * @return string
+ */
+ public function get_username() {
+ return $this->data['other']['username'];
+ }
+
+ /**
+ * Init method.
+ *
+ * @return void
+ */
+ protected function init() {
+ $this->context = \context_system::instance();
+ $this->data['crud'] = 'r';
+ $this->data['level'] = 50; // TODO MDL-37658.
+ $this->data['objecttable'] = 'user';
+ }
+
+ /**
+ * Custom validation.
+ *
+ * @throws coding_exception when validation does not pass.
+ * @return void
+ */
+ protected function validate_data() {
+ if (!isset($this->data['objectid'])) {
+ throw new \coding_exception("objectid has to be specified.");
+ } else if (!isset($this->data['other']['username'])) {
+ throw new \coding_exception("other['username'] has to be specified.");
+ }
+ }
+
+}
View
1 auth/db/tests/db_test.php
@@ -52,6 +52,7 @@ protected function init_auth_database() {
set_config('sybasequoting', '1', 'auth/db');
break;
+ case 'mariadb_native_moodle_database':
case 'mysqli_native_moodle_database':
set_config('type', 'mysqli', 'auth/db');
set_config('setupsql', "SET NAMES 'UTF-8'", 'auth/db');
View
75 auth/tests/auth_test.php
@@ -0,0 +1,75 @@
+<?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/>.
+
+/**
+ * Tests for auth.
+ *
+ * @package core_auth
+ * @copyright 2013 Frédéric Massart
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+global $CFG;
+require_once($CFG->libdir . '/authlib.php');
+
+/**
+ * Auth testcase class.
+ *
+ * @package core_auth
+ * @copyright 2013 Frédéric Massart
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class auth_testcase extends advanced_testcase {
+
+ public function test_user_loggedin_event() {
+ global $USER;
+ $this->resetAfterTest(true);
+ $this->setAdminUser();
+
+ $sink = $this->redirectEvents();
+ $user = clone($USER);
+ login_attempt_valid($user);
+ $events = $sink->get_events();
+ $sink->close();
+
+ $this->assertCount(1, $events);
+ $event = reset($events);
+ $this->assertInstanceOf('\core_auth\event\user_loggedin', $event);
+ $this->assertEquals('user', $event->objecttable);
+ $this->assertEquals('2', $event->objectid);
+ $this->assertEquals(context_system::instance()->id, $event->contextid);
+ $this->assertEquals($user, $event->get_record_snapshot('user', 2));
+ }
+
+ public function test_user_loggedin_event_exceptions() {
+ try {
+ $event = \core_auth\event\user_loggedin::create(array('objectid' => 1));
+ $this->fail('\core_auth\event\user_loggedin requires other[\'username\']');
+ } catch(Exception $e) {
+ $this->assertInstanceOf('coding_exception', $e);
+ }
+
+ try {
+ $event = \core_auth\event\user_loggedin::create(array('other' => array('username' => 'test')));
+ $this->fail('\core_auth\event\user_loggedin requires objectid');
+ } catch(Exception $e) {
+ $this->assertInstanceOf('coding_exception', $e);
+ }
+ }
+
+}
View
10 auth/tests/behat/behat_auth.php
@@ -49,10 +49,10 @@ class behat_auth extends behat_base {
public function i_log_in_as($username) {
return array(new Given('I am on homepage'),
- new Given('I follow "Log in"'),
- new Given('I fill in "Username" with "'.$username.'"'),
- new Given('I fill in "Password" with "'.$username.'"'),
- new Given('I press "Log in"')
+ 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') . '"')
);
}
@@ -62,7 +62,7 @@ public function i_log_in_as($username) {
* @Given /^I log out$/
*/
public function i_log_out() {
- return new When('I follow "Log out"');
+ return new When('I follow "' . get_string('logout') . '"');
}
}
View
23 backup/backupfilesedit_form.php
@@ -1,5 +1,4 @@
<?php
-
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
@@ -24,16 +23,32 @@
require_once($CFG->libdir.'/formslib.php');
class backup_files_edit_form extends moodleform {
- function definition() {
+
+ /**
+ * Form definition.
+ */
+ public function definition() {
$mform =& $this->_form;
- $contextid = $this->_customdata['contextid'];
- $options = array('subdirs'=>0, 'maxfiles'=>-1, 'accepted_types'=>'*', 'return_types'=>FILE_INTERNAL | FILE_REFERENCE);
+
+ $options = array('subdirs' => 0, 'maxfiles' => -1, 'accepted_types' => '*', 'return_types' => FILE_INTERNAL | FILE_REFERENCE);
+
$mform->addElement('filemanager', 'files_filemanager', get_string('files'), null, $options);
+
$mform->addElement('hidden', 'contextid', $this->_customdata['contextid']);
+ $mform->setType('contextid', PARAM_INT);
+
$mform->addElement('hidden', 'currentcontext', $this->_customdata['currentcontext']);
+ $mform->setType('currentcontext', PARAM_INT);
+
$mform->addElement('hidden', 'filearea', $this->_customdata['filearea']);
+ $mform->setType('filearea', PARAM_AREA);
+
$mform->addElement('hidden', 'component', $this->_customdata['component']);
+ $mform->setType('component', PARAM_COMPONENT);
+
$mform->addElement('hidden', 'returnurl', $this->_customdata['returnurl']);
+ $mform->setType('returnurl', PARAM_URL);
+
$this->add_action_buttons(true, get_string('savechanges'));
$this->set_data($this->_customdata['data']);
}
View
2 backup/cc/cc112moodle.php
@@ -66,7 +66,7 @@ public function generate_moodle_xml () {
$cdir = static::$path_to_manifest_folder . DIRECTORY_SEPARATOR . 'course_files';
if (!file_exists($cdir)) {
- mkdir($cdir);
+ mkdir($cdir, $CFG->directorypermissions, true);
}
$sheet_base = static::loadsheet(SHEET_BASE);
View
2 backup/cc/cc2moodle.php
@@ -149,7 +149,7 @@ public function generate_moodle_xml () {
$cdir = static::$path_to_manifest_folder . DIRECTORY_SEPARATOR . 'course_files';
if (!file_exists($cdir)) {
- mkdir($cdir);
+ mkdir($cdir, $CFG->directorypermissions, true);
}
$sheet_base = static::loadsheet(SHEET_BASE);
View
8 backup/cc/cc_lib/cc_utils.php
@@ -63,11 +63,13 @@ public static function uuidgen($prefix = '', $suffix = '', $uppercase = true) {
* @return mixed - directory short name or false in case of faliure
*/
public static function randomdir($where, $prefix = '', $suffix = '') {
+ global $CFG;
+
$dirname = false;
$randomname = self::uuidgen($prefix, $suffix, false);
$newdirname = $where.DIRECTORY_SEPARATOR.$randomname;
if (mkdir($newdirname)) {
- chmod($newdirname, 0755);
+ chmod($newdirname, $CFG->directorypermissions);
$dirname = $randomname;
}
return $dirname;
@@ -155,6 +157,8 @@ public static function embedded_mapping($packageroot, $contextid = null) {
}
public static function add_files(cc_i_manifest &$manifest, $packageroot, $outdir, $allinone = true) {
+ global $CFG;
+
if (pkg_static_resources::instance()->finished) {
return;
}
@@ -170,7 +174,7 @@ public static function add_files(cc_i_manifest &$manifest, $packageroot, $outdir
//let us try to recreate them
$justdir = $rdir->fullpath(false).$values[7];
if (!file_exists($justdir)) {
- if (!mkdir($justdir, 0777, true)) {
+ if (!mkdir($justdir, $CFG->directorypermissions, true)) {
throw new RuntimeException('Unable to create directories!');
}
}
View
3 backup/cc/cc_lib/gral_lib/pathutils.php
@@ -156,6 +156,7 @@ function pathDiff($path1, $path2) {
*/
function copyr($source, $dest)
{
+ global $CFG;
// Simple copy for a file
if (is_file($source)) {
return copy($source, $dest);
@@ -163,7 +164,7 @@ function copyr($source, $dest)
// Make destination directory
if (!is_dir($dest)) {
- mkdir($dest);
+ mkdir($dest, $CFG->directorypermissions, true);
}
// Loop through the folder
View
2 backup/cc/entities.class.php
@@ -266,7 +266,7 @@ protected function get_all_files () {
$fpath = $dpath . DIRECTORY_SEPARATOR . $rfpath;
if (!file_exists($dpath)) {
- mkdir($dpath);
+ mkdir($dpath, $CFG->directorypermissions, true);
}
//copy the folder.gif file
$folder_gif = "{$CFG->dirroot}/pix/i/files.gif";
View
2 backup/cc/entities11.class.php
@@ -65,7 +65,7 @@ protected function get_all_files () {
$rfpath = 'files.gif';
$fpath = $dpath . DIRECTORY_SEPARATOR . 'files.gif';
if (!file_exists($dpath)) {
- mkdir($dpath);
+ mkdir($dpath, $CFG->directorypermissions, true);
}
//copy the folder.gif file
$folder_gif = "{$CFG->dirroot}/pix/i/files.gif";
View
2 backup/converter/imscc11/backuplib.php
@@ -141,7 +141,7 @@ protected function define_execution() {
$tempdir = $CFG->dataroot . '/temp/backup/' . uniqid('', true);
- if (mkdir($tempdir, 0777, true)) {
+ if (mkdir($tempdir, $CFG->directorypermissions, true)) {
cc_convert_moodle2::convert($basepath, $tempdir);
//Switch the directories
View
19 backup/util/dbops/restore_dbops.class.php
@@ -614,13 +614,20 @@ public static function prechek_precheck_qbanks_by_level($restoreid, $courseid, $
} else {
self::set_backup_ids_record($restoreid, 'question_category', $category->id, $matchcat->id, $targetcontext->id);
$questions = self::restore_get_questions($restoreid, $category->id);
+
+ // Collect all the questions for this category into memory so we only talk to the DB once.
+ $questioncache = $DB->get_records_sql_menu("SELECT ".$DB->sql_concat('stamp', "' '", 'version').", id
+ FROM {question}
+ WHERE category = ?", array($matchcat->id));
+
foreach ($questions as $question) {
- $matchq = $DB->get_record('question', array(
- 'category' => $matchcat->id,
- 'stamp' => $question->stamp,
- 'version' => $question->version));
+ if (isset($questioncache[$question->stamp." ".$question->version])) {
+ $matchqid = $questioncache[$question->stamp." ".$question->version];
+ } else {
+ $matchqid = false;
+ }
// 5a) No match, check if user can add q
- if (!$matchq) {
+ if (!$matchqid) {
// 6a) User can, mark the q to be created
if ($canadd) {
// Nothing to mark, newitemid means create
@@ -645,7 +652,7 @@ public static function prechek_precheck_qbanks_by_level($restoreid, $courseid, $
// 5b) Match, mark q to be mapped
} else {
- self::set_backup_ids_record($restoreid, 'question', $question->id, $matchq->id);
+ self::set_backup_ids_record($restoreid, 'question', $question->id, $matchqid);
}
}
}
View
2 backup/util/helper/backup_helper.class.php
@@ -259,7 +259,7 @@ static public function store_backup_file($backupid, $filepath) {
if (@rename($filepath, $filedest)) {
return null;
}
- umask(0000);
+ umask($CFG->umaskpermissions);
if (copy($filepath, $filedest)) {
@chmod($filedest, $CFG->filepermissions); // may fail because the permissions may not make sense outside of dataroot
unlink($filepath);
View
4 backup/util/ui/tests/behat/backup_courses.feature
@@ -31,7 +31,7 @@ Feature: Backup Moodle courses
And I should not see "Section 3"
And I press "Continue"
And I click on "Continue" "button" in the ".bcs-current-course" "css_element"
- And I click on "//div[contains(concat(' ', @class, ' '), ' fitem ')][contains(., 'Include calendar events')]/descendant::img" "xpath_element"
- And I click on "setting_root_logs" "checkbox" in the "//div[contains(@class, 'fitem')][contains(., 'Include course logs')]" "xpath_element"
+ And "//div[contains(concat(' ', normalize-space(@class), ' '), ' fitem ')][contains(., 'Include calendar events')]/descendant::img" "xpath_element" should exists
+ And I check "Include course logs"
And I press "Cancel"
And I click on "Cancel" "button" in the ".confirmation-dialogue" "css_element"
View
79 backup/util/ui/tests/behat/behat_backup.php
@@ -61,25 +61,25 @@ public function i_backup_course_using_this_options($backupcourse, $options = fal
$this->find_link($backupcourse)->click();
// Click the backup link.
- $this->find_link('Backup')->click();
+ $this->find_link(get_string('backup'))->click();
// Initial settings.
$this->fill_backup_restore_form($options);
- $this->find_button('Next')->press();
+ $this->find_button(get_string('backupstage1action', 'backup'))->press();
// Schema settings.
$this->fill_backup_restore_form($options);
- $this->find_button('Next')->press();
+ $this->find_button(get_string('backupstage2action', 'backup'))->press();
// Confirmation and review, backup filename can also be specified.
$this->fill_backup_restore_form($options);
- $this->find_button('Perform backup')->press();
+ $this->find_button(get_string('backupstage4action', 'backup'))->press();
// Waiting for it to finish.
$this->wait(10);
// Last backup continue button.
- $this->find_button('Continue')->press();
+ $this->find_button(get_string('backupstage16action', 'backup'))->press();
}
/**
@@ -105,36 +105,37 @@ public function i_import_course_into_course($fromcourse, $tocourse, $options = f
// Click the course link.
$this->find_link($tocourse)->click();
- // Click the backup link.
- $this->find_link('Import')->click();
+ // Click the import link.
+ $this->find_link(get_string('import'))->click();
// Select the course.
$exception = new ExpectationException('"' . $fromcourse . '" course not found in the list of courses to import from', $this->getSession());
- $fromcourse = str_replace("'", "\'", $fromcourse);
- $xpath = "//div[contains(concat(' ', @class, ' '), ' ics-results ')]" .
- "/descendant::tr[contains(., '" . $fromcourse . "')]" .
+ // The argument should be converted to an xpath literal.
+ $fromcourse = $this->getSession()->getSelectorsHandler()->xpathLiteral($fromcourse);
+ $xpath = "//div[contains(concat(' ', normalize-space(@class), ' '), ' ics-results ')]" .
+ "/descendant::tr[contains(., $fromcourse)]" .
"/descendant::input[@type='radio']";
$radionode = $this->find('xpath', $xpath, $exception);
$radionode->check();
$radionode->click();
- $this->find_button('Continue')->press();
+ $this->find_button(get_string('continue'))->press();
// Initial settings.
$this->fill_backup_restore_form($options);
- $this->find_button('Next')->press();
+ $this->find_button(get_string('importbackupstage1action', 'backup'))->press();
// Schema settings.
$this->fill_backup_restore_form($options);
- $this->find_button('Next')->press();
+ $this->find_button(get_string('importbackupstage2action', 'backup'))->press();
// Run it.
- $this->find_button('Perform import')->press();
+ $this->find_button(get_string('importbackupstage4action', 'backup'))->press();
$this->wait();
// Continue and redirect to 'to' course.
- $this->find_button('Continue')->press();
+ $this->find_button(get_string('continue'))->press();
}
/**
@@ -150,17 +151,20 @@ public function i_restore_backup_into_course_using_this_options($backupfilename,
// Confirm restore.
$this->select_backup($backupfilename);
+ // The argument should be converted to an xpath literal.
+ $existingcourse = $this->getSession()->getSelectorsHandler()->xpathLiteral($existingcourse);
+
// Selecting the specified course (we can not call behat_forms::select_radio here as is in another behat subcontext).
- $existingcourse = str_replace("'", "\'", $existingcourse);
- $radionode = $this->find('xpath', "//div[contains(@class, 'bcs-existing-course')]" .
+ $radionode = $this->find('xpath', "//div[contains(concat(' ', normalize-space(@class), ' '), ' bcs-existing-course ')]" .
"/descendant::div[@class='restore-course-search']" .
- "/descendant::tr[contains(., '" . $existingcourse . "')]" .
+ "/descendant::tr[contains(., $existingcourse)]" .
"/descendant::input[@type='radio']");
$radionode->check();
$radionode->click();
// Pressing the continue button of the restore into an existing course section.
- $continuenode = $this->find('xpath', "//div[contains(@class, 'bcs-existing-course')]/descendant::input[@type='submit'][@value='Continue']");
+ $continuenode = $this->find('xpath', "//div[contains(concat(' ', normalize-space(@class), ' '), ' bcs-existing-course ')]" .
+ "/descendant::input[@type='submit'][@value='" . get_string('continue') . "']");
$continuenode->click();
$this->wait();
@@ -181,14 +185,15 @@ public function i_restore_backup_into_a_new_course_using_this_options($backupfil
$this->select_backup($backupfilename);
// The first category in the list.
- $radionode = $this->find('xpath', "//div[contains(@class, 'bcs-new-course')]" .
+ $radionode = $this->find('xpath', "//div[contains(concat(' ', normalize-space(@class), ' '), ' bcs-new-course ')]" .
"/descendant::div[@class='restore-course-search']" .
"/descendant::input[@type='radio']");
$radionode->check();
$radionode->click();
// Pressing the continue button of the restore into an existing course section.
- $continuenode = $this->find('xpath', "//div[contains(@class, 'bcs-new-course')]/descendant::input[@type='submit'][@value='Continue']");
+ $continuenode = $this->find('xpath', "//div[contains(concat(' ', normalize-space(@class), ' '), ' bcs-new-course ')]" .
+ "/descendant::input[@type='submit'][@value='" . get_string('continue') . "']");
$continuenode->click();
$this->wait();
@@ -209,14 +214,14 @@ public function i_merge_backup_into_the_current_course($backupfilename, $options
$this->select_backup($backupfilename);
// Merge without deleting radio option.
- $radionode = $this->find('xpath', "//div[contains(@class, 'bcs-current-course')]" .
+ $radionode = $this->find('xpath', "//div[contains(concat(' ', normalize-space(@class), ' '), 'bcs-current-course')]" .
"/descendant::input[@type='radio'][@name='target'][@value='1']");
$radionode->check();
$radionode->click();
// Pressing the continue button of the restore merging section.
- $continuenode = $this->find('xpath', "//div[contains(@class, 'bcs-current-course')]" .
- "/descendant::input[@type='submit'][@value='Continue']");
+ $continuenode = $this->find('xpath', "//div[contains(concat(' ', normalize-space(@class), ' '), 'bcs-current-course')]" .
+ "/descendant::input[@type='submit'][@value='" . get_string('continue') . "']");
$continuenode->click();
$this->wait();
@@ -237,14 +242,14 @@ public function i_merge_backup_into_current_course_deleting_its_contents($backup
$this->select_backup($backupfilename);
// Delete contents radio option.
- $radionode = $this->find('xpath', "//div[contains(@class, 'bcs-current-course')]" .
+ $radionode = $this->find('xpath', "//div[contains(concat(' ', normalize-space(@class), ' '), 'bcs-current-course')]" .
"/descendant::input[@type='radio'][@name='target'][@value='0']");
$radionode->check();
$radionode->click();
// Pressing the continue button of the restore merging section.
- $continuenode = $this->find('xpath', "//div[contains(@class, 'bcs-current-course')]" .
- "/descendant::input[@type='submit'][@value='Continue']");
+ $continuenode = $this->find('xpath', "//div[contains(concat(' ', normalize-space(@class), ' '), 'bcs-current-course')]" .
+ "/descendant::input[@type='submit'][@value='" . get_string('continue') . "']");
$continuenode->click();
$this->wait();
@@ -263,12 +268,16 @@ protected function select_backup($backupfilename) {
// Using xpath as there are other restore links before this one.
$exception = new ExpectationException('The "' . $backupfilename . '" backup file can not be found in this page', $this->getSession());
- $xpath = "//tr[contains(., '" . $backupfilename . "')]/descendant::a[contains(., 'Restore')]";
+
+ // The argument should be converted to an xpath literal.
+ $backupfilename = $this->getSession()->getSelectorsHandler()->xpathLiteral($backupfilename);
+
+ $xpath = "//tr[contains(., $backupfilename)]/descendant::a[contains(., '" . get_string('restore') . "')]";
$restorelink = $this->find('xpath', $xpath, $exception);
$restorelink->click();
// Confirm the backup contents.
- $restore = $this->find_button('Continue')->press();
+ $restore = $this->find_button(get_string('continue'))->press();
}
/**
@@ -284,18 +293,18 @@ protected function process_restore($options) {
// Settings.
$this->fill_backup_restore_form($options);
- $this->find_button('Next')->press();
+ $this->find_button(get_string('restorestage4action', 'backup'))->press();
// Schema.
$this->fill_backup_restore_form($options);
- $this->find_button('Next')->press();
+ $this->find_button(get_string('restorestage8action', 'backup'))->press();
// Review, no options here.
- $this->find_button('Perform restore')->press();
+ $this->find_button(get_string('restorestage16action', 'backup'))->press();
$this->wait(10);
// Last restore continue button, redirected to restore course after this.
- $this->find_button('Continue')->press();
+ $this->find_button(get_string('restorestage32action', 'backup'))->press();
}
/**
@@ -339,6 +348,10 @@ protected function fill_backup_restore_form($options) {
*/
protected function wait($timeout = false) {
+ if (!$this->running_javascript()) {
+ return;
+ }
+
if (!$timeout) {
$timeout = self::TIMEOUT;
}
View
47 backup/util/xml/parser/processors/grouped_parser_processor.class.php
@@ -43,6 +43,18 @@
protected $groupedpaths; // Paths we are requesting grouped
protected $currentdata; // Where we'll be acummulating data
+ /**
+ * Keep cache of parent directory paths for XML parsing.
+ * @var array
+ */
+ protected $parentcache = array();
+
+ /**
+ * Remaining space for parent directory paths.
+ * @var integer
+ */
+ protected $parentcacheavailablesize = 2048;
+
public function __construct(array $paths = array()) {
$this->groupedpaths = array();
$this->currentdata = null;
@@ -65,7 +77,7 @@ public function add_path($path, $grouped = false) {
$a->child = $found;
throw new progressive_parser_exception('xml_grouped_child_found', $a);
}
- $this->groupedpaths[] = $path;
+ $this->groupedpaths[$path] = true;
}
parent::add_path($path);
}
@@ -141,7 +153,7 @@ protected function postprocess_chunk($data) {
}
protected function path_is_grouped($path) {
- return in_array($path, $this->groupedpaths);
+ return isset($this->groupedpaths[$path]);
}
/**
@@ -150,24 +162,49 @@ protected function path_is_grouped($path) {
* false if not
*/
protected function grouped_parent_exists($path) {
- $parentpath = progressive_parser::dirname($path);
+ $parentpath = $this->get_parent_path($path);
+
while ($parentpath != '/') {
if ($this->path_is_grouped($parentpath)) {
return $parentpath;
}
- $parentpath = progressive_parser::dirname($parentpath);
+ $parentpath = $this->get_parent_path($parentpath);
}
return false;
}
/**
+ * Get the parent path using a local cache for performance.
+ *
+ * @param $path string The pathname you wish to obtain the parent name for.
+ * @return string The parent pathname.
+ */
+ protected function get_parent_path($path) {
+ if (!isset($this->parentcache[$path])) {
+ $this->parentcache[$path] = progressive_parser::dirname($path);
+ $this->parentcacheavailablesize--;
+ if ($this->parentcacheavailablesize < 0) {
+ // Older first is cheaper than LRU. We use 10% as items are grouped together and the large quiz
+ // restore from MDL-40585 used only 600 parent paths. This is an XML heirarchy, so common paths
+ // are grouped near each other. eg; /question_bank/question_category/question/element. After keeping
+ // question_bank paths in the cache when we move to another area and the question_bank cache is not
+ // useful any longer.
+ $this->parentcache = array_slice($this->parentcache, 200, null, true);
+ $this->parentcacheavailablesize += 200;
+ }
+ }
+ return $this->parentcache[$path];
+ }
+
+
+ /**
* Function that will look for any grouped
* child for the given path, returning it if found,
* false if not
*/
protected function grouped_child_exists($path) {
$childpath = $path . '/';
- foreach ($this->groupedpaths as $groupedpath) {
+ foreach ($this->groupedpaths as $groupedpath => $set) {
if (strpos($groupedpath, $childpath) === 0) {
return $groupedpath;
}
View
15 badges/backpack.js
@@ -2,7 +2,20 @@
* Push badges to backpack.
*/
function addtobackpack(event, args) {
- OpenBadges.issue([args.assertion], function(errors, successes) { });
+ var badgetable = Y.one('#issued-badge-table');
+ var errordiv = Y.one('#addtobackpack-error');
+ var errortext = M.util.get_string('error:backpackproblem', 'badges');
+ var errorhtml = '<div id="addtobackpack-error" class="box boxaligncenter notifyproblem">' + errortext + '</div>';
+
+ if (typeof OpenBadges !== 'undefined') {
+ OpenBadges.issue([args.assertion], function(errors, successes) { });
+ } else {
+ // Add error div if it doesn't exist yet.
+ if (!errordiv) {
+ var badgerror = Y.Node.create(errorhtml);
+ badgetable.insert(badgerror, 'before');
+ }
+ }
}
/**
View
6 badges/badge.php
@@ -56,10 +56,8 @@
navigation_node::override_active_url($url);
}
-// TODO: Better way of pushing badges to Mozilla backpack?
-if (!empty($CFG->badges_allowexternalbackpack)) {
- $PAGE->requires->js(new moodle_url('http://backpack.openbadges.org/issuer.js'), true);
-}
+// Include JS files for backpack support.
+badges_setup_backpack_js();
echo $OUTPUT->header();
View
7 badges/mybadges.php
@@ -90,11 +90,8 @@
$PAGE->set_heading($title);
$PAGE->set_pagelayout('mydashboard');
-// TODO: Better way of pushing badges to Mozilla backpack?
-if (!empty($CFG->badges_allowexternalbackpack)) {
- $PAGE->requires->js(new moodle_url('http://backpack.openbadges.org/issuer.js'), true);
- $PAGE->requires->js('/badges/backpack.js', true);
-}
+// Include JS files for backpack support.
+badges_setup_backpack_js();
$output = $PAGE->get_renderer('core', 'badges');
$badges = badges_get_user_badges($USER->id);
View
11 badges/renderer.php
@@ -282,6 +282,7 @@ protected function render_issued_badge(issued_badge $ibadge) {
$today = strtotime($today_date);
$table = new html_table();
+ $table->id = 'issued-badge-table';
$imagetable = new html_table();
$imagetable->attributes = array('class' => 'clearfix badgeissuedimage');
@@ -294,11 +295,13 @@ protected function render_issued_badge(issued_badge $ibadge) {
$expiration = isset($issued['expires']) ? strtotime($issued['expires']) : $today + 1;
if (!empty($CFG->badges_allowexternalbackpack) && ($expiration > $today) && badges_user_has_backpack($USER->id)) {
$assertion = new moodle_url('/badges/assertion.php', array('b' => $ibadge->hash));
+ $action = new component_action('click', 'addtobackpack', array('assertion' => $assertion->out(false)));
$attributes = array(
- 'type' => 'button',
- 'value' => get_string('addtobackpack', 'badges'),
- 'onclick' => 'OpenBadges.issue(["' . $assertion->out(false) . '"], function(errors, successes) { })');
+ 'type' => 'button',
+ 'id' => 'addbutton',
+ 'value' => get_string('addtobackpack', 'badges'));
$tobackpack = html_writer::tag('input', '', $attributes);
+ $this->output->add_action_handler($action, 'addbutton');
$imagetable->data[] = array($tobackpack);
}
}
@@ -459,7 +462,7 @@ protected function render_badge_user_collection(badge_user_collection $badges) {
get_string('downloadall'), 'POST', array('class' => 'activatebadge'));
// Local badges.
- $localhtml = html_writer::start_tag('fieldset', array('class' => 'generalbox'));
+ $localhtml = html_writer::start_tag('fieldset', array('id' => 'issued-badge-table', 'class' => 'generalbox'));
$localhtml .= html_writer::tag('legend',
$this->output->heading_with_help(get_string('localbadges', 'badges', $SITE->fullname), 'localbadgesh', 'badges'));
if ($badges->badges) {
View
11 blocks/comments/tests/behat/behat_block_comments.php
@@ -63,7 +63,7 @@ public function i_add_comment_to_comments_block($comment) {
$commentstextarea = $this->find('css', '.comment-area textarea', $exception);
$commentstextarea->setValue($comment);
- $this->find_link('Save comment')->click();
+ $this->find_link(get_string('savecomment'))->click();
// Wait for the AJAX request.
$this->getSession()->wait(4 * 1000, false);
@@ -91,8 +91,11 @@ public function i_delete_comment_from_comments_block($comment) {
$exception = new ElementNotFoundException($this->getSession(), '"' . $comment . '" comment ');
- $commentxpath = "//div[contains(concat(' ', @class, ' '), ' block_comments ')]" .
- "/descendant::div[@class='comment-message'][contains(., '" . $comment . "')]";
+ // Using xpath liternal to avoid possible problems with comments containing quotes.
+ $commentliteral = $this->getSession()->getSelectorsHandler()->xpathLiteral($comment);
+
+ $commentxpath = "//div[contains(concat(' ', normalize-space(@class), ' '), ' block_comments ')]" .
+ "/descendant::div[@class='comment-message'][contains(., $commentliteral)]";
$commentnode = $this->find('xpath', $commentxpath, $exception);
// Click on delete icon.
@@ -101,7 +104,7 @@ public function i_delete_comment_from_comments_block($comment) {
$deleteicon->click();
// Yes confirm.
- $confirmnode = $this->find('xpath', "//div[@class='comment-delete-confirm']/descendant::a[contains(., 'Yes')]");
+ $confirmnode = $this->find('xpath', "//div[@class='comment-delete-confirm']/descendant::a[contains(., '" . get_string('yes') . "')]");
$confirmnode->click();
// Wait for the AJAX request.
View
6 blocks/course_overview/renderer.php
@@ -103,10 +103,10 @@ public function course_overview($courses, $overviews) {
// No need to pass title through s() here as it will be done automatically by html_writer.
$attributes = array('title' => $course->fullname);
- if (empty($course->visible)) {
- $attributes['class'] = 'dimmed';
- }
if ($course->id > 0) {
+ if (empty($course->visible)) {
+ $attributes['class'] = 'dimmed';
+ }
$courseurl = new moodle_url('/course/view.php', array('id' => $course->id));
$coursefullname = format_string($course->fullname, true, $course->id);
$link = html_writer::link($courseurl, $coursefullname, $attributes);
View
8 blocks/recent_activity/block_recent_activity.php
@@ -100,13 +100,17 @@ protected function get_timestart() {
}
/**
- * Returns all recent enrollments
+ * Returns all recent enrolments.
+ *
+ * This function previously used get_recent_enrolments located in lib/deprecatedlib.php which would
+ * return an empty array which was identified in MDL-36993. The use of this function outside the
+ * deprecated lib was removed in MDL-40649.
*
* @todo MDL-36993 this function always return empty array
* @return array array of entries from {user} table
*/
protected function get_recent_enrolments() {
- return get_recent_enrolments($this->page->course->id, $this->get_timestart());
+ return array();
}
/**
View
4 blocks/tests/behat/behat_blocks.php
@@ -46,13 +46,13 @@ class behat_blocks extends behat_base {
* @param string $blockname
*/
public function i_add_the_block($blockname) {
- $steps = new Given('I select "' . $blockname . '" from "bui_addblock"');
+ $steps = new Given('I select "' . $this->escape($blockname) . '" from "bui_addblock"');
// If we are running without javascript we need to submit the form.
if (!$this->running_javascript()) {
$steps = array(
$steps,
- new Given('I click on "Go" "button" in the "#add_block" "css_element"')
+ new Given('I click on "' . get_string('go') . '" "button" in the "#add_block" "css_element"')
);
}
return $steps;
View
2 blocks/tests/behat/configure_block_throughout_site.feature
@@ -32,4 +32,4 @@ Feature: Add and configure blocks throughout the site
And I press "Save changes"
And I follow "Course 1"
# The first block matching the pattern should be top-left block
- And I should see "Comments" in the "//*[@id='region-pre']/descendant::div[contains(concat(' ', @class, ' '), ' block ')]" "xpath_element"
+ And I should see "Comments" in the "//*[@id='region-pre']/descendant::div[contains(concat(' ', normalize-space(@class), ' '), ' block ')]" "xpath_element"
View
1 cache/locallib.php
@@ -108,6 +108,7 @@ protected function config_save() {
fflush($handle);
fclose($handle);
$locking->unlock('configwrite', 'config');
+ @chmod($cachefile, $CFG->filepermissions);
// Tell PHP to recompile the script.
core_component::invalidate_opcode_php_cache($cachefile);
} else {
View
1 cache/stores/file/lib.php
@@ -720,6 +720,7 @@ protected function write_file($file, $content) {
// Finally rename the temp file to the desired file, returning the true|false result.
$result = rename($tempfile, $file);
+ @chmod($file, $this->cfg->filepermissions);
if (!$result) {
// Failed to rename, don't leave files lying around.
@unlink($tempfile);
View
3 cache/stores/memcache/addinstanceform.php
@@ -49,7 +49,8 @@ protected function configuration_definition() {
$form->addElement('text', 'prefix', get_string('prefix', 'cachestore_memcache'),
array('maxlength' => 5, 'size' => 5));
$form->addHelpButton('prefix', 'prefix', 'cachestore_memcache');
- $form->setType('prefix', PARAM_ALPHAEXT);
+ $form->setType('prefix', PARAM_TEXT); // We set to text but we have a rule to limit to alphanumext.
$form->setDefault('prefix', 'mdl_');
+ $form->addRule('prefix', get_string('prefixinvalid', 'cachestore_memcache'), 'regex', '#^[a-zA-Z0-9\-_]+$#');
}
}
View
1 cache/stores/memcache/lang/en/cachestore_memcache.php
@@ -31,6 +31,7 @@
$string['prefix_help'] = 'This prefix is used for all key names on the memcache server.
* If you only have one Moodle instance using this server, you can leave this value default.
* Due to key length restrictions, a maximum of 5 characters is permitted.';
+$string['prefixinvalid'] = 'Invalid prefix. You can only use a-z A-Z 0-9-_.';
$string['servers'] = 'Servers';
$string['servers_help'] = 'This sets the servers that should be utilised by this memcache adapter.
Servers should be defined one per line and consist of a server address and optionally a port and weight.
View
2 cache/stores/memcache/lib.php
@@ -404,7 +404,7 @@ public function instance_deleted() {
* Generates an instance of the cache store that can be used for testing.
*
* @param cache_definition $definition
- * @return false
+ * @return cachestore_memcache|false
*/
public static function initialise_test_instance(cache_definition $definition) {
if (!self::are_requirements_met()) {
View
33 cache/stores/memcache/tests/memcache_test.php
@@ -59,4 +59,35 @@ public function setUp() {
protected function get_class_name() {
return 'cachestore_memcache';
}
-}
+
+ /**
+ * Tests the valid keys to ensure they work.
+ */
+ public function test_valid_keys() {
+ $definition = cache_definition::load_adhoc(cache_store::MODE_APPLICATION, 'cachestore_memcache', 'phpunit_test');
+ $instance = cachestore_memcache::initialise_test_instance($definition);
+
+ if (!$instance) { // Something prevented memcache store to be inited (extension, TEST_CACHESTORE_MEMCACHE_TESTSERVERS...).
+ $this->markTestSkipped();
+ }
+
+ $keys = array(
+ // Alphanumeric.
+ 'abc', 'ABC', '123', 'aB1', '1aB',
+ // Hyphens.
+ 'a-1', '1-a', '-a1', 'a1-',
+ // Underscores.
+ 'a_1', '1_a'