Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Merge branch 'master' into backup-convert

Conflicts:
	backup/util/xml/parser/processors/simplified_parser_processor.class.php
	backup/util/xml/parser/simpletest/testparser.php
  • Loading branch information...
commit f6f7a7266c641407261d1220eae14b5e71fdbdbe 2 parents 6cfa5a3 + 16b5541
David Mudrák authored

Showing 266 changed files with 3,511 additions and 930 deletions. Show diff stats Hide diff stats

  1. 34  backup/moodle2/backup_coursereport_plugin.class.php
  2. 1  backup/moodle2/backup_plan_builder.class.php
  3. 4  backup/moodle2/backup_stepslib.php
  4. 29  backup/moodle2/restore_coursereport_plugin.class.php
  5. 2  backup/moodle2/restore_plan_builder.class.php
  6. 3  backup/moodle2/restore_stepslib.php
  7. 4  backup/util/xml/parser/processors/simplified_parser_processor.class.php
  8. 30  backup/util/xml/parser/simpletest/testparser.php
  9. 92  blocks/dock.js
  10. 1  blocks/navigation/renderer.php
  11. 116  blocks/navigation/yui/navigation/navigation.js
  12. 1  lang/en/error.php
  13. 12  lib/db/install.xml
  14. 50  lib/db/upgrade.php
  15. 181  lib/outputrenderers.php
  16. 2  lib/rsslib.php
  17. 2  message/lib.php
  18. 2  mod/assignment/lib.php
  19. 3  mod/chat/lib.php
  20. 2  mod/choice/lib.php
  21. 2  mod/choice/renderer.php
  22. 8  mod/data/backup/moodle2/backup_data_stepslib.php
  23. 8  mod/data/backup/moodle2/restore_data_stepslib.php
  24. 23  mod/data/db/upgrade.php
  25. 147  mod/data/lib.php
  26. 4  mod/data/version.php
  27. 5  mod/data/view.php
  28. 1  mod/feedback/lib.php
  29. 2  mod/folder/lib.php
  30. 8  mod/forum/backup/moodle2/backup_forum_stepslib.php
  31. 8  mod/forum/backup/moodle2/restore_forum_stepslib.php
  32. 22  mod/forum/db/upgrade.php
  33. 300  mod/forum/lib.php
  34. 69  mod/forum/user.php
  35. 8  mod/forum/version.php
  36. 4  mod/forum/view.php
  37. 8  mod/glossary/backup/moodle2/backup_glossary_stepslib.php
  38. 9  mod/glossary/backup/moodle2/restore_glossary_stepslib.php
  39. 23  mod/glossary/db/upgrade.php
  40. 4  mod/glossary/deleteentry.php
  41. 153  mod/glossary/lib.php
  42. 4  mod/glossary/version.php
  43. 6  mod/glossary/view.php
  44. 2  mod/imscp/lib.php
  45. 2  mod/label/lib.php
  46. 4  mod/lesson/lib.php
  47. 2  mod/page/lib.php
  48. 4  mod/quiz/lib.php
  49. 2  mod/resource/lib.php
  50. 3  mod/survey/lib.php
  51. 2  mod/url/lib.php
  52. 2  mod/wiki/lib.php
  53. 2  mod/workshop/lib.php
  54. 67  rating/index.php
  55. 997  rating/lib.php
  56. 51  rating/rate.php
  57. 93  rating/rate_ajax.php
  58. 6  repository/webdav/lib.php
  59. 133  theme/afterburner/config.php
  60. 55  theme/afterburner/lang/en/theme_afterburner.php
  61. 128  theme/afterburner/layout/default.php
  62. 23  theme/afterburner/layout/embedded.php
  63. BIN  theme/afterburner/pix/core/bg.png
  64. BIN  theme/afterburner/pix/core/bground.jpg
  65. BIN  theme/afterburner/pix/core/h2grad.jpg
  66. BIN  theme/afterburner/pix/favicon.ico
  67. BIN  theme/afterburner/pix/footer/moodle-logo.png
  68. BIN  theme/afterburner/pix/forum/gradient.png
  69. BIN  theme/afterburner/pix/images/light3.png
  70. BIN  theme/afterburner/pix/menu/ab-arrowover.png
  71. BIN  theme/afterburner/pix/menu/nav-arrow-right.png
  72. BIN  theme/afterburner/pix/screenshot.jpg
  73. BIN  theme/afterburner/pix/sideblocks/sidegrad.jpg
  74. BIN  theme/afterburner/pix/tab/left.gif
  75. BIN  theme/afterburner/pix/tab/left_active.gif
  76. BIN  theme/afterburner/pix/tab/left_active_hover.gif
  77. BIN  theme/afterburner/pix/tab/left_hover.gif
  78. BIN  theme/afterburner/pix/tab/right.gif
  79. BIN  theme/afterburner/pix/tab/right_active.gif
  80. BIN  theme/afterburner/pix/tab/right_active_hover.gif
  81. BIN  theme/afterburner/pix/tab/right_end.gif
  82. BIN  theme/afterburner/pix/tab/right_hover.gif
  83. BIN  theme/afterburner/pix/tab/right_last.gif
  84. BIN  theme/afterburner/pix/tab/rtlbg.gif
  85. BIN  theme/afterburner/pix/tab/tabrow1.gif
  86. BIN  theme/afterburner/pix_core/a/em1_bwgreater.gif
  87. BIN  theme/afterburner/pix_core/a/em1_greater.gif
  88. BIN  theme/afterburner/pix_core/a/em1_lesser.gif
  89. BIN  theme/afterburner/pix_core/a/em1_raquo.gif
  90. BIN  theme/afterburner/pix_core/a/help.png
  91. BIN  theme/afterburner/pix_core/a/l_breadcrumb.gif
  92. BIN  theme/afterburner/pix_core/a/logout.png
  93. BIN  theme/afterburner/pix_core/a/r_breadcrumb.gif
  94. BIN  theme/afterburner/pix_core/a/r_go.gif
  95. BIN  theme/afterburner/pix_core/a/r_next.gif
  96. BIN  theme/afterburner/pix_core/a/r_previous.gif
  97. BIN  theme/afterburner/pix_core/a/refresh.png
  98. BIN  theme/afterburner/pix_core/a/search.png
  99. BIN  theme/afterburner/pix_core/a/setting.png
  100. BIN  theme/afterburner/pix_core/c/course.png
  101. BIN  theme/afterburner/pix_core/c/event.png
  102. BIN  theme/afterburner/pix_core/c/groups.png
  103. BIN  theme/afterburner/pix_core/c/site.png
  104. BIN  theme/afterburner/pix_core/c/user.png
  105. BIN  theme/afterburner/pix_core/docs.png
  106. BIN  theme/afterburner/pix_core/f/access.png
  107. BIN  theme/afterburner/pix_core/f/avi.png
  108. BIN  theme/afterburner/pix_core/f/excel.png
  109. BIN  theme/afterburner/pix_core/f/flash.png
  110. BIN  theme/afterburner/pix_core/f/folder-32.png
  111. BIN  theme/afterburner/pix_core/f/folder.png
  112. BIN  theme/afterburner/pix_core/f/help.png
  113. BIN  theme/afterburner/pix_core/f/html.png
  114. BIN  theme/afterburner/pix_core/f/image-32.png
  115. BIN  theme/afterburner/pix_core/f/image.png
  116. BIN  theme/afterburner/pix_core/f/odb.png
  117. BIN  theme/afterburner/pix_core/f/odf.png
  118. BIN  theme/afterburner/pix_core/f/odg.png
  119. BIN  theme/afterburner/pix_core/f/odm.png
  120. BIN  theme/afterburner/pix_core/f/odp.png
  121. BIN  theme/afterburner/pix_core/f/ods.png
  122. BIN  theme/afterburner/pix_core/f/odt.png
  123. BIN  theme/afterburner/pix_core/f/pdf.png
  124. BIN  theme/afterburner/pix_core/f/powerpoint.png
  125. BIN  theme/afterburner/pix_core/f/text-32.png
  126. BIN  theme/afterburner/pix_core/f/text.png
  127. BIN  theme/afterburner/pix_core/f/unknown-32.png
  128. BIN  theme/afterburner/pix_core/f/unknown.png
  129. BIN  theme/afterburner/pix_core/f/video.png
  130. BIN  theme/afterburner/pix_core/f/web.png
  131. BIN  theme/afterburner/pix_core/f/word.png
  132. BIN  theme/afterburner/pix_core/f/xml.png
  133. BIN  theme/afterburner/pix_core/f/zip.png
  134. BIN  theme/afterburner/pix_core/g/f1.png
  135. BIN  theme/afterburner/pix_core/g/f2.png
  136. BIN  theme/afterburner/pix_core/g/user100.png
  137. BIN  theme/afterburner/pix_core/g/user35.png
  138. BIN  theme/afterburner/pix_core/help.png
  139. BIN  theme/afterburner/pix_core/i/all.png
  140. BIN  theme/afterburner/pix_core/i/backup.png
  141. BIN  theme/afterburner/pix_core/i/calc.png
  142. BIN  theme/afterburner/pix_core/i/checkpermissions.png
  143. BIN  theme/afterburner/pix_core/i/course.png
  144. BIN  theme/afterburner/pix_core/i/db.png
  145. BIN  theme/afterburner/pix_core/i/edit.gif
  146. BIN  theme/afterburner/pix_core/i/email.png
  147. BIN  theme/afterburner/pix_core/i/feedback.png
  148. BIN  theme/afterburner/pix_core/i/feedback_add.png
  149. BIN  theme/afterburner/pix_core/i/files.png
  150. BIN  theme/afterburner/pix_core/i/filter.png
  151. BIN  theme/afterburner/pix_core/i/flagged.png
  152. BIN  theme/afterburner/pix_core/i/grades.png
  153. BIN  theme/afterburner/pix_core/i/group.png
  154. BIN  theme/afterburner/pix_core/i/hide.png
  155. BIN  theme/afterburner/pix_core/i/info.png
  156. BIN  theme/afterburner/pix_core/i/lock.png
  157. BIN  theme/afterburner/pix_core/i/marker.png
  158. BIN  theme/afterburner/pix_core/i/menu.png
  159. BIN  theme/afterburner/pix_core/i/new.png
  160. BIN  theme/afterburner/pix_core/i/news.png
  161. BIN  theme/afterburner/pix_core/i/one.png
  162. BIN  theme/afterburner/pix_core/i/payment.png
  163. BIN  theme/afterburner/pix_core/i/permissions.png
  164. BIN  theme/afterburner/pix_core/i/publish.png
  165. BIN  theme/afterburner/pix_core/i/questions.png
  166. BIN  theme/afterburner/pix_core/i/report.png
  167. BIN  theme/afterburner/pix_core/i/restore.png
  168. BIN  theme/afterburner/pix_core/i/return.png
  169. BIN  theme/afterburner/pix_core/i/roles.png
  170. BIN  theme/afterburner/pix_core/i/scales.png
  171. BIN  theme/afterburner/pix_core/i/settings.png
  172. BIN  theme/afterburner/pix_core/i/show.png
  173. BIN  theme/afterburner/pix_core/i/unflagged.png
  174. BIN  theme/afterburner/pix_core/i/user.png
  175. BIN  theme/afterburner/pix_core/i/users.png
  176. BIN  theme/afterburner/pix_core/icon.png
  177. BIN  theme/afterburner/pix_core/m/USD.gif
  178. 1  theme/afterburner/pix_core/s/SMILEYS
  179. BIN  theme/afterburner/pix_core/s/angry.png
  180. BIN  theme/afterburner/pix_core/s/approve.png
  181. BIN  theme/afterburner/pix_core/s/biggrin.png
  182. BIN  theme/afterburner/pix_core/s/blackeye.gif
  183. BIN  theme/afterburner/pix_core/s/blush.png
  184. BIN  theme/afterburner/pix_core/s/clown.gif
  185. BIN  theme/afterburner/pix_core/s/cool.png
  186. BIN  theme/afterburner/pix_core/s/dead.png
  187. BIN  theme/afterburner/pix_core/s/egg.gif
  188. BIN  theme/afterburner/pix_core/s/evil.png
  189. BIN  theme/afterburner/pix_core/s/heart.png
  190. BIN  theme/afterburner/pix_core/s/kiss.png
  191. BIN  theme/afterburner/pix_core/s/martin.gif
  192. BIN  theme/afterburner/pix_core/s/mixed.png
  193. BIN  theme/afterburner/pix_core/s/no.gif
  194. BIN  theme/afterburner/pix_core/s/sad.png
  195. BIN  theme/afterburner/pix_core/s/shy.png
  196. BIN  theme/afterburner/pix_core/s/sleepy.png
  197. BIN  theme/afterburner/pix_core/s/smiley.png
  198. BIN  theme/afterburner/pix_core/s/surprise.png
  199. BIN  theme/afterburner/pix_core/s/thoughtful.png
  200. BIN  theme/afterburner/pix_core/s/tongueout.png
  201. BIN  theme/afterburner/pix_core/s/wideeyes.png
  202. BIN  theme/afterburner/pix_core/s/wink.png
  203. BIN  theme/afterburner/pix_core/s/yes.png
  204. BIN  theme/afterburner/pix_core/t/adddir.png
  205. BIN  theme/afterburner/pix_core/t/addfile.png
  206. BIN  theme/afterburner/pix_core/t/addgreen.png
  207. BIN  theme/afterburner/pix_core/t/backup.png
  208. BIN  theme/afterburner/pix_core/t/block.png
  209. BIN  theme/afterburner/pix_core/t/calendar.png
  210. BIN  theme/afterburner/pix_core/t/clear.png
  211. BIN  theme/afterburner/pix_core/t/copy.png
  212. BIN  theme/afterburner/pix_core/t/delete.png
  213. BIN  theme/afterburner/pix_core/t/down.png
  214. BIN  theme/afterburner/pix_core/t/download.png
  215. BIN  theme/afterburner/pix_core/t/edit.png
  216. BIN  theme/afterburner/pix_core/t/email.png
  217. BIN  theme/afterburner/pix_core/t/emailno.png
  218. BIN  theme/afterburner/pix_core/t/feedback.png
  219. BIN  theme/afterburner/pix_core/t/feedback_add.png
  220. BIN  theme/afterburner/pix_core/t/go.png
  221. BIN  theme/afterburner/pix_core/t/groupn.png
  222. BIN  theme/afterburner/pix_core/t/groups.png
  223. BIN  theme/afterburner/pix_core/t/hide.png
  224. BIN  theme/afterburner/pix_core/t/lock.png
  225. BIN  theme/afterburner/pix_core/t/move.png
  226. BIN  theme/afterburner/pix_core/t/preview.png
  227. BIN  theme/afterburner/pix_core/t/restore.png
  228. BIN  theme/afterburner/pix_core/t/show.png
  229. BIN  theme/afterburner/pix_core/t/stop.png
  230. BIN  theme/afterburner/pix_core/t/unlock.png
  231. BIN  theme/afterburner/pix_core/t/up.png
  232. BIN  theme/afterburner/pix_core/t/user.png
  233. BIN  theme/afterburner/pix_core/t/userblue.png
  234. BIN  theme/afterburner/pix_core/t/usernot.png
  235. BIN  theme/afterburner/pix_core/u/f1.png
  236. BIN  theme/afterburner/pix_core/u/f2.png
  237. BIN  theme/afterburner/pix_plugins/enrol/self/withoutkey.png
  238. BIN  theme/afterburner/pix_plugins/mod/assignment/icon.png
  239. BIN  theme/afterburner/pix_plugins/mod/chat/icon.png
  240. BIN  theme/afterburner/pix_plugins/mod/choice/icon.png
  241. BIN  theme/afterburner/pix_plugins/mod/data/icon.png
  242. BIN  theme/afterburner/pix_plugins/mod/folder/icon.png
  243. BIN  theme/afterburner/pix_plugins/mod/forum/icon.png
  244. BIN  theme/afterburner/pix_plugins/mod/glossary/icon.png
  245. BIN  theme/afterburner/pix_plugins/mod/imscp/icon.png
  246. BIN  theme/afterburner/pix_plugins/mod/label/icon.png
  247. BIN  theme/afterburner/pix_plugins/mod/lesson/icon.png
  248. BIN  theme/afterburner/pix_plugins/mod/page/icon.png
  249. BIN  theme/afterburner/pix_plugins/mod/quiz/icon.png
  250. BIN  theme/afterburner/pix_plugins/mod/resource/icon.png
  251. BIN  theme/afterburner/pix_plugins/mod/scorm/icon.png
  252. BIN  theme/afterburner/pix_plugins/mod/survey/icon.png
  253. BIN  theme/afterburner/pix_plugins/mod/url/icon.png
  254. BIN  theme/afterburner/pix_plugins/mod/wiki/icon.png
  255. BIN  theme/afterburner/pix_plugins/mod/workshop/icon.png
  256. 139  theme/afterburner/renderers.php
  257. 155  theme/afterburner/style/afterburner_blocks.css
  258. 167  theme/afterburner/style/afterburner_calendar.css
  259. 125  theme/afterburner/style/afterburner_dock.css
  260. 148  theme/afterburner/style/afterburner_layout.css
  261. 193  theme/afterburner/style/afterburner_menu.css
  262. 86  theme/afterburner/style/afterburner_mod.css
  263. 436  theme/afterburner/style/afterburner_styles.css
  264. 7  theme/afterburner/style/rtl.css
  265. 5  theme/base/style/core.css
  266. 4  version.php
34  backup/moodle2/backup_coursereport_plugin.class.php
... ...
@@ -0,0 +1,34 @@
  1
+<?php
  2
+// This file is part of Moodle - http://moodle.org/
  3
+//
  4
+// Moodle is free software: you can redistribute it and/or modify
  5
+// it under the terms of the GNU General Public License as published by
  6
+// the Free Software Foundation, either version 3 of the License, or
  7
+// (at your option) any later version.
  8
+//
  9
+// Moodle is distributed in the hope that it will be useful,
  10
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
  11
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12
+// GNU General Public License for more details.
  13
+//
  14
+// You should have received a copy of the GNU General Public License
  15
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  16
+
  17
+defined('MOODLE_INTERNAL') || die();
  18
+
  19
+/**
  20
+ * Base class for course report backup plugins.
  21
+ *
  22
+ * NOTE: When you back up a course, it potentially may run backup for all
  23
+ * course reports. In order to control whether a particular report gets
  24
+ * backed up, a course report should make use of the second and third
  25
+ * parameters in get_plugin_element().
  26
+ *
  27
+ * @package    moodlecore
  28
+ * @subpackage backup-moodle2
  29
+ * @copyright  2011 onwards The Open University
  30
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  31
+ */
  32
+abstract class backup_coursereport_plugin extends backup_plugin {
  33
+    // Use default parent behaviour
  34
+}
1  backup/moodle2/backup_plan_builder.class.php
@@ -36,6 +36,7 @@
36 36
 require_once($CFG->dirroot . '/backup/moodle2/backup_qtype_plugin.class.php');
37 37
 require_once($CFG->dirroot . '/backup/moodle2/backup_format_plugin.class.php');
38 38
 require_once($CFG->dirroot . '/backup/moodle2/backup_theme_plugin.class.php');
  39
+require_once($CFG->dirroot . '/backup/moodle2/backup_coursereport_plugin.class.php');
39 40
 require_once($CFG->dirroot . '/backup/moodle2/backup_plagiarism_plugin.class.php');
40 41
 require_once($CFG->dirroot . '/backup/moodle2/backup_subplugin.class.php');
41 42
 require_once($CFG->dirroot . '/backup/moodle2/backup_settingslib.php');
4  backup/moodle2/backup_stepslib.php
@@ -419,6 +419,10 @@ protected function define_structure() {
419 419
         // save course data (in case of user theme, legacy theme, etc)
420 420
         $this->add_plugin_structure('theme', $course, true);
421 421
 
  422
+        // attach course report plugin structure to $course element; multiple
  423
+        // course reports can save course data if required
  424
+        $this->add_plugin_structure('coursereport', $course, true);
  425
+
422 426
         // attach plagiarism plugin structure to $course element, only one allowed
423 427
         $this->add_plugin_structure('plagiarism', $course, false);
424 428
 
29  backup/moodle2/restore_coursereport_plugin.class.php
... ...
@@ -0,0 +1,29 @@
  1
+<?php
  2
+// This file is part of Moodle - http://moodle.org/
  3
+//
  4
+// Moodle is free software: you can redistribute it and/or modify
  5
+// it under the terms of the GNU General Public License as published by
  6
+// the Free Software Foundation, either version 3 of the License, or
  7
+// (at your option) any later version.
  8
+//
  9
+// Moodle is distributed in the hope that it will be useful,
  10
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
  11
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12
+// GNU General Public License for more details.
  13
+//
  14
+// You should have received a copy of the GNU General Public License
  15
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  16
+
  17
+defined('MOODLE_INTERNAL') || die();
  18
+
  19
+/**
  20
+ * Restore for course plugin: course report.
  21
+ *
  22
+ * @package    moodlecore
  23
+ * @subpackage backup-moodle2
  24
+ * @copyright 2011 The Open University
  25
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  26
+ */
  27
+abstract class restore_coursereport_plugin extends restore_plugin {
  28
+    // Use default parent behaviour
  29
+}
2  backup/moodle2/restore_plan_builder.class.php
@@ -35,11 +35,13 @@
35 35
 require_once($CFG->dirroot . '/backup/moodle2/restore_qtype_plugin.class.php');
36 36
 require_once($CFG->dirroot . '/backup/moodle2/restore_format_plugin.class.php');
37 37
 require_once($CFG->dirroot . '/backup/moodle2/restore_theme_plugin.class.php');
  38
+require_once($CFG->dirroot . '/backup/moodle2/restore_coursereport_plugin.class.php');
38 39
 require_once($CFG->dirroot . '/backup/moodle2/restore_plagiarism_plugin.class.php');
39 40
 require_once($CFG->dirroot . '/backup/moodle2/backup_plugin.class.php');
40 41
 require_once($CFG->dirroot . '/backup/moodle2/backup_qtype_plugin.class.php');
41 42
 require_once($CFG->dirroot . '/backup/moodle2/backup_format_plugin.class.php');
42 43
 require_once($CFG->dirroot . '/backup/moodle2/backup_theme_plugin.class.php');
  44
+require_once($CFG->dirroot . '/backup/moodle2/backup_coursereport_plugin.class.php');
43 45
 require_once($CFG->dirroot . '/backup/moodle2/backup_plagiarism_plugin.class.php');
44 46
 require_once($CFG->dirroot . '/backup/moodle2/restore_subplugin.class.php');
45 47
 require_once($CFG->dirroot . '/backup/moodle2/restore_settingslib.php');
3  backup/moodle2/restore_stepslib.php
@@ -1010,6 +1010,9 @@ protected function define_structure() {
1010 1010
         // Apply for 'theme' plugins optional paths at course level
1011 1011
         $this->add_plugin_structure('theme', $course);
1012 1012
 
  1013
+        // Apply for 'course report' plugins optional paths at course level
  1014
+        $this->add_plugin_structure('coursereport', $course);
  1015
+
1013 1016
         // Apply for plagiarism plugins optional paths at course level
1014 1017
         $this->add_plugin_structure('plagiarism', $course);
1015 1018
 
4  backup/util/xml/parser/processors/simplified_parser_processor.class.php
@@ -174,12 +174,12 @@ public function before_path($path) {
174 174
      */
175 175
     public function after_path($path) {
176 176
         $toprocess = false;
177  
-        // If the path being closed matches (same or parent) the last path in the stack
  177
+        // If the path being closed matches (same or parent) the first path in the stack
178 178
         // we process pending startend notifications until one matching end is found
179 179
         if ($element = reset($this->startendinfo)) {
180 180
             $elepath = $element['path'];
181 181
             $eleaction = $element['action'];
182  
-            if ($eleaction = 'end' && strpos($elepath, $path) === 0) {
  182
+            if (strpos($elepath, $path) === 0) {
183 183
                 $toprocess = true;
184 184
             }
185 185
 
30  backup/util/xml/parser/simpletest/testparser.php
@@ -652,43 +652,45 @@ function test_grouped_david_backup19_file_fragment() {
652 652
     function helper_check_notifications_order_integrity($notifications) {
653 653
         $numerrors = 0;
654 654
         $notifpile = array('pilebase' => 'start');
655  
-        $lastpile = 'start:pilebase';
  655
+        $lastnotif = 'start:pilebase';
656 656
         foreach ($notifications as $notif) {
657  
-            $lastpilelevel = strlen(preg_replace('/[^\/]/', '', $lastpile));
658  
-            $lastpiletype  = preg_replace('/:.*/', '', $lastpile);
659  
-            $lastpilepath  = preg_replace('/.*:/', '', $lastpile);
660 657
 
661  
-            $notiflevel = strlen(preg_replace('/[^\/]/', '', $notif));
  658
+            $lastpiletype = end($notifpile);
  659
+            $lastpilepath = key($notifpile);
  660
+            $lastpilelevel = strlen(preg_replace('/[^\/]/', '', $lastpilepath));
  661
+
  662
+            $lastnotiftype  = preg_replace('/:.*/', '', $lastnotif);
  663
+            $lastnotifpath  = preg_replace('/.*:/', '', $lastnotif);
  664
+            $lastnotiflevel = strlen(preg_replace('/[^\/]/', '', $lastnotifpath));
  665
+
662 666
             $notiftype  = preg_replace('/:.*/', '', $notif);
663 667
             $notifpath  = preg_replace('/.*:/', '', $notif);
  668
+            $notiflevel = strlen(preg_replace('/[^\/]/', '', $notifpath));
664 669
 
665 670
             switch ($notiftype) {
666 671
                 case 'process':
667  
-                    if ($lastpilepath != $notifpath or $lastpiletype != 'start') {
668  
-                        $numerrors++; // Only start for same path is allowed before process
  672
+                    if ($lastnotifpath != $notifpath or $lastnotiftype != 'start') {
  673
+                        $numerrors++; // Only start for same path from last notification is allowed before process
669 674
                     }
670 675
                     $notifpile[$notifpath] = 'process'; // Update the status in the pile
671 676
                     break;
672 677
                 case 'end':
673 678
                     if ($lastpilepath != $notifpath or ($lastpiletype != 'process' and $lastpiletype != 'start')) {
674  
-                        $numerrors++; // Only process for same path is allowed before end
  679
+                        $numerrors++; // Only process and start for same path from last pile is allowed before end
675 680
                     }
676 681
                     unset($notifpile[$notifpath]); // Delete from the pile
677 682
                     break;
678 683
                 case 'start':
679 684
                     if (array_key_exists($notifpath, $notifpile) or $notiflevel <= $lastpilelevel) {
680  
-                        $numerrors++; // If same path exists or the level is < than the last one
  685
+                        $numerrors++; // Only non existing in pile and with level > last pile is allowed on start
681 686
                     }
682 687
                     $notifpile[$notifpath] = 'start'; // Add to the pile
683 688
                     break;
684 689
                 default:
685 690
                     $numerrors++; // Incorrect type of notification => error
686 691
             }
687  
-            // Update lastpile
688  
-            end($notifpile);
689  
-            $path = key($notifpile);
690  
-            $type = $notifpile[$path];
691  
-            $lastpile = $type. ':' . $path;
  692
+            // Update lastnotif
  693
+            $lastnotif = $notif;
692 694
         }
693 695
         return $numerrors;
694 696
     }
92  blocks/dock.js
@@ -66,7 +66,75 @@ M.core_dock.init = function(Y) {
66 66
     // Give the dock item class the event properties/methods
67 67
     Y.augment(this.item, Y.EventTarget);
68 68
     Y.augment(this, Y.EventTarget, true);
  69
+    /**
  70
+     * A 'dock:actionkey' Event.
  71
+     * The event consists of the left arrow, right arrow, enter and space keys.
  72
+     * More keys can be mapped to action meanings.
  73
+     * actions: collapse , expand, toggle, enter.
  74
+     *
  75
+     * This event is subscribed to by dockitems.
  76
+     * The on() method to subscribe allows specifying the desired trigger actions as JSON.
  77
+     *
  78
+     * This event can also be delegated if needed.
  79
+     * Todo: This could be centralised, a similar Event is defined in blocks/navigation/yui/navigation/navigation.js
  80
+     */
  81
+    Y.Event.define("dock:actionkey", {
  82
+        // Webkit and IE repeat keydown when you hold down arrow keys.
  83
+        // Opera links keypress to page scroll; others keydown.
  84
+        // Firefox prevents page scroll via preventDefault() on either
  85
+        // keydown or keypress.
  86
+        _event: (Y.UA.webkit || Y.UA.ie) ? 'keydown' : 'keypress',
  87
+
  88
+        _keys: {
  89
+            //arrows
  90
+            '37': 'collapse',
  91
+            '39': 'expand',
  92
+            //(@todo: lrt/rtl/M.core_dock.cfg.orientation decision to assign arrow to meanings)
  93
+            '32': 'toggle',
  94
+            '13': 'enter'
  95
+        },
  96
+
  97
+        _keyHandler: function (e, notifier, args) {
  98
+            if (!args.actions) {
  99
+                var actObj = {collapse:true, expand:true, toggle:true, enter:true};
  100
+            } else {
  101
+                var actObj = args.actions;
  102
+            }
  103
+            if (this._keys[e.keyCode] && actObj[this._keys[e.keyCode]]) {
  104
+                e.action = this._keys[e.keyCode];
  105
+                notifier.fire(e);
  106
+            }
  107
+        },
  108
+
  109
+        on: function (node, sub, notifier) {
  110
+            // subscribe to _event and ask keyHandler to handle with given args[0] (the desired actions).
  111
+            if (sub.args == null) {
  112
+                //no actions given
  113
+                sub._detacher = node.on(this._event, this._keyHandler,this, notifier, {actions:false});
  114
+            } else {
  115
+                sub._detacher = node.on(this._event, this._keyHandler,this, notifier, sub.args[0]);
  116
+            }
  117
+        },
69 118
 
  119
+        detach: function (node, sub, notifier) {
  120
+            //detach our _detacher handle of the subscription made in on()
  121
+            sub._detacher.detach();
  122
+        },
  123
+
  124
+        delegate: function (node, sub, notifier, filter) {
  125
+            // subscribe to _event and ask keyHandler to handle with given args[0] (the desired actions).
  126
+            if (sub.args == null) {
  127
+                //no actions given
  128
+                sub._delegateDetacher = node.delegate(this._event, this._keyHandler,filter, this, notifier, {actions:false});
  129
+            } else {
  130
+                sub._delegateDetacher = node.delegate(this._event, this._keyHandler,filter, this, notifier, sub.args[0]);
  131
+            }
  132
+        },
  133
+
  134
+        detachDelegate: function (node, sub, notifier) {
  135
+            sub._delegateDetacher.detach();
  136
+        }
  137
+    });
70 138
     // Publish the events the dock has
71 139
     this.publish('dock:beforedraw', {prefix:'dock'});
72 140
     this.publish('dock:beforeshow', {prefix:'dock'});
@@ -125,9 +193,10 @@ M.core_dock.init = function(Y) {
125 193
 
126 194
     // Add a removeall button
127 195
     // Must set the image src seperatly of we get an error with XML strict headers
128  
-    var removeall = Y.Node.create('<img alt="'+M.str.block.undockall+'" title="'+M.str.block.undockall+'" />');
  196
+    var removeall = Y.Node.create('<img alt="'+M.str.block.undockall+'" title="'+M.str.block.undockall+'" tabindex="0"/>');
129 197
     removeall.setAttribute('src',this.cfg.removeallicon);
130 198
     removeall.on('removeall|click', this.remove_all, this);
  199
+    removeall.on('dock:actionkey', this.remove_all, this, {actions:{enter:true}});
131 200
     this.nodes.buttons.appendChild(Y.Node.create('<div class="'+css.controls+'"></div>').append(removeall));
132 201
 
133 202
     // Create a manager for the height of the tabs. Once set this can be forgotten about
@@ -590,7 +659,7 @@ M.core_dock.resetFirstItem = function() {
590 659
  * @function
591 660
  * @return {boolean}
592 661
  */
593  
-M.core_dock.remove_all = function() {
  662
+M.core_dock.remove_all = function(e) {
594 663
     for (var i in this.items) {
595 664
         this.remove(i);
596 665
     }
@@ -840,9 +909,10 @@ M.core_dock.genericblock.prototype = {
840 909
             }, this);
841 910
             // Add a close icon
842 911
             // Must set the image src seperatly of we get an error with XML strict headers
843  
-            var closeicon = Y.Node.create('<span class="hidepanelicon"><img alt="" style="width:11px;height:11px;cursor:pointer;" /></span>');
  912
+            var closeicon = Y.Node.create('<span class="hidepanelicon" tabindex="0"><img alt="" style="width:11px;height:11px;cursor:pointer;" /></span>');
844 913
             closeicon.one('img').setAttribute('src', M.util.image_url('t/dockclose', 'moodle'));
845 914
             closeicon.on('forceclose|click', this.hide, this);
  915
+            closeicon.on('dock:actionkey',this.hide, this, {actions:{enter:true,toggle:true}});
846 916
             this.commands.append(closeicon);
847 917
         }, dockitem);
848 918
         // Register an event so that when it is removed we can put it back as a block
@@ -955,7 +1025,8 @@ M.core_dock.item.prototype = {
955 1025
 
956 1026
         this.nodes.docktitle = Y.Node.create('<div id="dock_item_'+this.id+'_title" class="'+css.dockedtitle+'"></div>');
957 1027
         this.nodes.docktitle.append(this.title);
958  
-        this.nodes.dockitem = Y.Node.create('<div id="dock_item_'+this.id+'" class="'+css.dockeditem+'"></div>');
  1028
+        this.nodes.dockitem = Y.Node.create('<div id="dock_item_'+this.id+'" class="'+css.dockeditem+'" tabindex="0"></div>');
  1029
+        this.nodes.dockitem.on('dock:actionkey', this.toggle, this);
959 1030
         if (M.core_dock.count === 1) {
960 1031
             this.nodes.dockitem.addClass('firstdockitem');
961 1032
         }
@@ -1000,6 +1071,19 @@ M.core_dock.item.prototype = {
1000 1071
         this.fire('dockeditem:hidecomplete');
1001 1072
     },
1002 1073
     /**
  1074
+     * A toggle between calling show and hide functions based on css.activeitem
  1075
+     * Applies rules to key press events (dock:actionkey)
  1076
+     * @param {Event} e
  1077
+     */
  1078
+    toggle : function(e) {
  1079
+        var css = M.core_dock.css;
  1080
+        if (this.nodes.docktitle.hasClass(css.activeitem) && !(e.type == 'dock:actionkey' && e.action=='expand')) {
  1081
+            this.hide();
  1082
+        } else if (!this.nodes.docktitle.hasClass(css.activeitem) && !(e.type == 'dock:actionkey' && e.action=='collapse'))  {
  1083
+            this.show();
  1084
+        }
  1085
+    },
  1086
+    /**
1003 1087
      * This function removes the node and destroys it's bits
1004 1088
      * @param {Event} e
1005 1089
      */
1  blocks/navigation/renderer.php
@@ -52,6 +52,7 @@ protected function navigation_node($items, $attrs=array(), $expansionlimit=null,
52 52
                 $attributes['class'] = 'dimmed_text';
53 53
             }
54 54
             if (is_string($item->action) || empty($item->action) || ($item->type === navigation_node::TYPE_CATEGORY && empty($options['linkcategories']))) {
  55
+                $attributes['tabindex'] = '0'; //add tab support to span but still maintain character stream sequence.
55 56
                 $content = html_writer::tag('span', $content, $attributes);
56 57
             } else if ($item->action instanceof action_link) {
57 58
                 //TODO: to be replaced with something else
116  blocks/navigation/yui/navigation/navigation.js
... ...
@@ -1,5 +1,74 @@
1 1
 YUI.add('moodle-block_navigation-navigation', function(Y){
2 2
 
  3
+/**
  4
+ * A 'actionkey' Event to help with Y.delegate().
  5
+ * The event consists of the left arrow, right arrow, enter and space keys.
  6
+ * More keys can be mapped to action meanings.
  7
+ * actions: collapse , expand, toggle, enter.
  8
+ *
  9
+ * This event is delegated to branches in the navigation tree.
  10
+ * The on() method to subscribe allows specifying the desired trigger actions as JSON.
  11
+ *
  12
+ * Todo: This could be centralised, a similar Event is defined in blocks/dock.js
  13
+ */
  14
+Y.Event.define("actionkey", {
  15
+   // Webkit and IE repeat keydown when you hold down arrow keys.
  16
+    // Opera links keypress to page scroll; others keydown.
  17
+    // Firefox prevents page scroll via preventDefault() on either
  18
+    // keydown or keypress.
  19
+    _event: (Y.UA.webkit || Y.UA.ie) ? 'keydown' : 'keypress',
  20
+
  21
+    _keys: {
  22
+        //arrows
  23
+        '37': 'collapse',
  24
+        '39': 'expand',
  25
+        //(@todo: lrt/rtl/M.core_dock.cfg.orientation decision to assign arrow to meanings)
  26
+        '32': 'toggle',
  27
+        '13': 'enter'
  28
+    },
  29
+
  30
+    _keyHandler: function (e, notifier, args) {
  31
+        if (!args.actions) {
  32
+            var actObj = {collapse:true, expand:true, toggle:true, enter:true};
  33
+        } else {
  34
+            var actObj = args.actions;
  35
+        }
  36
+        if (this._keys[e.keyCode] && actObj[this._keys[e.keyCode]]) {
  37
+            e.action = this._keys[e.keyCode];
  38
+            notifier.fire(e);
  39
+        }
  40
+    },
  41
+
  42
+    on: function (node, sub, notifier) {
  43
+        // subscribe to _event and ask keyHandler to handle with given args[0] (the desired actions).
  44
+        if (sub.args == null) {
  45
+            //no actions given
  46
+            sub._detacher = node.on(this._event, this._keyHandler,this, notifier, {actions:false});
  47
+        } else {
  48
+            sub._detacher = node.on(this._event, this._keyHandler,this, notifier, sub.args[0]);
  49
+        }
  50
+    },
  51
+
  52
+    detach: function (node, sub, notifier) {
  53
+        //detach our _detacher handle of the subscription made in on()
  54
+        sub._detacher.detach();
  55
+    },
  56
+
  57
+    delegate: function (node, sub, notifier, filter) {
  58
+        // subscribe to _event and ask keyHandler to handle with given args[0] (the desired actions).
  59
+        if (sub.args == null) {
  60
+            //no actions given
  61
+            sub._delegateDetacher = node.delegate(this._event, this._keyHandler,filter, this, notifier, {actions:false});
  62
+        } else {
  63
+            sub._delegateDetacher = node.delegate(this._event, this._keyHandler,filter, this, notifier, sub.args[0]);
  64
+        }
  65
+    },
  66
+
  67
+    detachDelegate: function (node, sub, notifier) {
  68
+        sub._delegateDetacher.detach();
  69
+    }
  70
+});
  71
+
3 72
 var EXPANSIONLIMIT_EVERYTHING = 0,
4 73
     EXPANSIONLIMIT_COURSE     = 20,
5 74
     EXPANSIONLIMIT_SECTION    = 30,
@@ -36,6 +105,7 @@ TREE.prototype = {
36 105
         // Delegate event to toggle expansion
37 106
         var self = this;
38 107
         Y.delegate('click', function(e){self.toggleExpansion(e);}, node.one('.block_tree'), '.tree_item.branch');
  108
+        Y.delegate('actionkey', function(e){self.toggleExpansion(e);}, node.one('.block_tree'), '.tree_item.branch');
39 109
 
40 110
         // Gather the expandable branches ready for initialisation.
41 111
         var expansions = [];
@@ -71,8 +141,8 @@ TREE.prototype = {
71 141
         // First check if they managed to click on the li iteslf, then find the closest
72 142
         // LI ancestor and use that
73 143
 
74  
-        if (e.target.test('a')) {
75  
-            // A link has been clicked don't fire any more events just do the default.
  144
+        if (e.target.test('a') && (e.keyCode == 0 || e.keyCode == 13)) {
  145
+            // A link has been clicked (or keypress is 'enter') don't fire any more events just do the default.
76 146
             e.stopPropagation();
77 147
             return;
78 148
         }
@@ -88,7 +158,21 @@ TREE.prototype = {
88 158
 
89 159
         // Toggle expand/collapse providing its not a root level branch.
90 160
         if (!target.hasClass('depth_1')) {
91  
-            target.toggleClass('collapsed');
  161
+            if (e.type == 'actionkey') {
  162
+                switch (e.action) {
  163
+                    case 'expand' :
  164
+                        target.removeClass('collapsed');
  165
+                        break;
  166
+                    case 'collapse' :
  167
+                        target.addClass('collapsed');
  168
+                        break;
  169
+                    default :
  170
+                        target.toggleClass('collapsed');
  171
+                }
  172
+                e.halt();
  173
+            } else {
  174
+                target.toggleClass('collapsed');
  175
+            }
92 176
         }
93 177
 
94 178
         // If the accordian feature has been enabled collapse all siblings.
@@ -152,9 +236,10 @@ BRANCH.prototype = {
152 236
      */
153 237
     node : null,
154 238
     /**
155  
-     * A reference to the ajax load event handle when created.
  239
+     * A reference to the ajax load event handlers when created.
156 240
      */
157 241
     event_ajaxload : null,
  242
+    event_ajaxload_actionkey : null,
158 243
     /**
159 244
      * Initialises the branch when it is first created.
160 245
      */
@@ -193,8 +278,13 @@ BRANCH.prototype = {
193 278
 
194 279
         var isbranch = (this.get('expandable') || this.get('haschildren'));
195 280
         var branchli = Y.Node.create('<li></li>');
  281
+        var link = this.get('link');
196 282
         var branchp = Y.Node.create('<p class="tree_item"></p>').setAttribute('id', this.get('id'));
197  
-
  283
+        if (!link) {
  284
+            //add tab focus if not link (so still one focus per menu node).
  285
+            // it was suggested to have 2 foci. one for the node and one for the link in MDL-27428.
  286
+            branchp.setAttribute('tabindex', '0');
  287
+        }
198 288
         if (isbranch) {
199 289
             branchli.addClass('collapsed').addClass('contains_branch');
200 290
             branchp.addClass('branch');
@@ -220,7 +310,6 @@ BRANCH.prototype = {
220 310
             }
221 311
         }
222 312
 
223  
-        var link = this.get('link');
224 313
         if (!link) {
225 314
             if (branchicon) {
226 315
                 branchp.appendChild(branchicon);
@@ -253,6 +342,7 @@ BRANCH.prototype = {
253 342
         }
254 343
         if (this.get('expandable')) {
255 344
             this.event_ajaxload = this.node.on('ajaxload|click', this.ajaxLoad, this);
  345
+            this.event_ajaxload_actionkey = this.node.on('actionkey', this.ajaxLoad, this);
256 346
         }
257 347
         return this;
258 348
     },
@@ -274,7 +364,16 @@ BRANCH.prototype = {
274 364
      * request made here.
275 365
      */
276 366
     ajaxLoad : function(e) {
277  
-        e.stopPropagation();
  367
+        if (e.type == 'actionkey' && e.action != 'enter') {
  368
+            e.halt();
  369
+        } else {
  370
+            e.stopPropagation();
  371
+        }
  372
+        if (e.type = 'actionkey' && e.action == 'enter' && e.target.test('A')) {
  373
+            this.event_ajaxload_actionkey.detach();
  374
+            this.event_ajaxload.detach();
  375
+            return true; // no ajaxLoad for enter
  376
+        }
278 377
 
279 378
         if (this.node.hasClass('loadingbranch')) {
280 379
             return true;
@@ -307,6 +406,7 @@ BRANCH.prototype = {
307 406
     ajaxProcessResponse : function(tid, outcome) {
308 407
         this.node.removeClass('loadingbranch');
309 408
         this.event_ajaxload.detach();
  409
+        this.event_ajaxload_actionkey.detach();
310 410
         try {
311 411
             var object = Y.JSON.parse(outcome.responseText);
312 412
             if (object.children && object.children.length > 0) {
@@ -455,4 +555,4 @@ M.block_navigation = M.block_navigation || {
455 555
     }
456 556
 };
457 557
 
458  
-}, '@VERSION@', {requires:['base', 'core_dock', 'io', 'node', 'dom', 'event-custom', 'event-delegate', 'json-parse']});
  558
+}, '@VERSION@', {requires:['base', 'core_dock', 'io', 'node', 'dom', 'event-custom', 'event-delegate', 'json-parse']});
1  lang/en/error.php
@@ -303,6 +303,7 @@
303 303
 $string['invalidpasswordpolicy'] = 'Invalid password policy';
304 304
 $string['invalidpaymentmethod'] = 'Invalid payment method: {$a}';
305 305
 $string['invalidqueryparam'] = 'ERROR: Incorrect number of query parameters. Expected {$a->expected}, got {$a->actual}.';
  306
+$string['invalidratingarea'] = 'Invalid rating area';
306 307
 $string['invalidrecord'] = 'Can not find data record in database table {$a}.';
307 308
 $string['invalidrecordunknown'] = 'Can not find data record in database.';
308 309
 $string['invalidrequest'] = 'Invalid request';
12  lib/db/install.xml
... ...
@@ -1,5 +1,5 @@
1 1
 <?xml version="1.0" encoding="UTF-8" ?>
2  
-<XMLDB PATH="lib/db" VERSION="20110209" COMMENT="XMLDB file for core Moodle tables"
  2
+<XMLDB PATH="lib/db" VERSION="20110523" COMMENT="XMLDB file for core Moodle tables"
3 3
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4 4
     xsi:noNamespaceSchemaLocation="../../lib/xmldb/xmldb.xsd"
5 5
 >
@@ -2591,8 +2591,10 @@
2591 2591
     <TABLE NAME="rating" COMMENT="moodle ratings" PREVIOUS="blog_external" NEXT="license">
2592 2592
       <FIELDS>
2593 2593
         <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="true" NEXT="contextid"/>
2594  
-        <FIELD NAME="contextid" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="false" PREVIOUS="id" NEXT="itemid"/>
2595  
-        <FIELD NAME="itemid" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="false" PREVIOUS="contextid" NEXT="scaleid"/>
  2594
+        <FIELD NAME="contextid" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="false" PREVIOUS="id" NEXT="component"/>
  2595
+        <FIELD NAME="component" TYPE="char" LENGTH="100" NOTNULL="true" SEQUENCE="false" PREVIOUS="contextid" NEXT="ratingarea"/>
  2596
+        <FIELD NAME="ratingarea" TYPE="char" LENGTH="50" NOTNULL="true" SEQUENCE="false" PREVIOUS="component" NEXT="itemid"/>
  2597
+        <FIELD NAME="itemid" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="false" PREVIOUS="ratingarea" NEXT="scaleid"/>
2596 2598
         <FIELD NAME="scaleid" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="false" SEQUENCE="false" PREVIOUS="itemid" NEXT="rating"/>
2597 2599
         <FIELD NAME="rating" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="false" PREVIOUS="scaleid" NEXT="userid"/>
2598 2600
         <FIELD NAME="userid" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="false" PREVIOUS="rating" NEXT="timecreated"/>
@@ -2605,7 +2607,7 @@
2605 2607
         <KEY NAME="userid" TYPE="foreign" FIELDS="userid" REFTABLE="user" REFFIELDS="id" COMMENT="Relates to user.id" PREVIOUS="contextid"/>
2606 2608
       </KEYS>
2607 2609
       <INDEXES>
2608  
-        <INDEX NAME="itemid" UNIQUE="false" FIELDS="itemid"/>
  2610
+        <INDEX NAME="uniqueuserrating" UNIQUE="false" FIELDS="component, ratingarea, contextid, itemid" COMMENT="These fields define a unique user rating of an item"/>
2609 2611
       </INDEXES>
2610 2612
     </TABLE>
2611 2613
     <TABLE NAME="license" COMMENT="store licenses used by moodle" PREVIOUS="rating" NEXT="registration_hubs">
@@ -2753,4 +2755,4 @@
2753 2755
       </KEYS>
2754 2756
     </TABLE>
2755 2757
   </TABLES>
2756  
-</XMLDB>
  2758
+</XMLDB>
50  lib/db/upgrade.php
@@ -6062,6 +6062,56 @@ function xmldb_main_upgrade($oldversion) {
6062 6062
         upgrade_main_savepoint(true, 2011022100.01);
6063 6063
     }
6064 6064
 
  6065
+    if ($oldversion < 2011052300.00) {
  6066
+        $table = new xmldb_table('rating');
  6067
+
  6068
+        // Add the component field to the ratings table
  6069
+        upgrade_set_timeout(60 * 20);
  6070
+        $field = new xmldb_field('component', XMLDB_TYPE_CHAR, '100', null, XMLDB_NOTNULL, null, 'unknown', 'contextid');
  6071
+        if (!$dbman->field_exists($table, $field)) {
  6072
+            $dbman->add_field($table, $field);
  6073
+        }
  6074
+
  6075
+        // Add the ratingarea field to the ratings table
  6076
+        upgrade_set_timeout(60 * 20);
  6077
+        $field = new xmldb_field('ratingarea', XMLDB_TYPE_CHAR, '50', null, XMLDB_NOTNULL, null, 'unknown', 'component');
  6078
+        if (!$dbman->field_exists($table, $field)) {
  6079
+            $dbman->add_field($table, $field);
  6080
+        }
  6081
+
  6082
+        upgrade_main_savepoint(true, 2011052300.00);
  6083
+    }
  6084
+
  6085
+    if ($oldversion < 2011052300.01) {
  6086
+
  6087
+        // Define index uniqueuserrating (unique) to be added to rating
  6088
+        $table = new xmldb_table('rating');
  6089
+        $index = new xmldb_index('uniqueuserrating', XMLDB_INDEX_NOTUNIQUE, array('component', 'ratingarea', 'contextid', 'itemid'));
  6090
+
  6091
+        // Conditionally launch add index uniqueuserrating
  6092
+        if (!$dbman->index_exists($table, $index)) {
  6093
+            $dbman->add_index($table, $index);
  6094
+        }
  6095
+
  6096
+        // Main savepoint reached
  6097
+        upgrade_main_savepoint(true, 2011052300.01);
  6098
+    }
  6099
+
  6100
+    if ($oldversion < 2011052300.02) {
  6101
+
  6102
+        // Define index itemid (not unique) to be dropped form rating
  6103
+        $table = new xmldb_table('rating');
  6104
+        $index = new xmldb_index('itemid', XMLDB_INDEX_NOTUNIQUE, array('itemid'));
  6105
+
  6106
+        // Conditionally launch drop index itemid
  6107
+        if ($dbman->index_exists($table, $index)) {
  6108
+            $dbman->drop_index($table, $index);
  6109
+        }
  6110
+
  6111
+        // Main savepoint reached
  6112
+        upgrade_main_savepoint(true, 2011052300.02);
  6113
+    }
  6114
+
6065 6115
 
6066 6116
     return true;
6067 6117
 }
181  lib/outputrenderers.php
@@ -1410,95 +1410,38 @@ protected function render_pix_emoticon(pix_emoticon $emoticon) {
1410 1410
     */
1411 1411
     function render_rating(rating $rating) {
1412 1412
         global $CFG, $USER;
1413  
-        static $havesetupjavascript = false;
1414 1413
 
1415  
-        if( $rating->settings->aggregationmethod == RATING_AGGREGATE_NONE ){
  1414
+        if ($rating->settings->aggregationmethod == RATING_AGGREGATE_NONE) {
1416 1415
             return null;//ratings are turned off
1417 1416
         }
1418 1417
 
1419  
-        $useajax = !empty($CFG->enableajax);
1420  
-
1421  
-        //include required Javascript
1422  
-        if( !$havesetupjavascript && $useajax ) {
1423  
-            $this->page->requires->js_init_call('M.core_rating.init');
1424  
-            $havesetupjavascript = true;
1425  
-        }
1426  
-
1427  
-        //check the item we're rating was created in the assessable time window
1428  
-        $inassessablewindow = true;
1429  
-        if ( $rating->settings->assesstimestart && $rating->settings->assesstimefinish ) {
1430  
-            if ($rating->itemtimecreated < $rating->settings->assesstimestart || $rating->itemtimecreated > $rating->settings->assesstimefinish) {
1431  
-                $inassessablewindow = false;
1432  
-            }
1433  
-        }
  1418
+        $ratingmanager = new rating_manager();
  1419
+        // Initialise the JavaScript so ratings can be done by AJAX.
  1420
+        $ratingmanager->initialise_rating_javascript($this->page);
1434 1421
 
1435 1422
         $strrate = get_string("rate", "rating");
1436 1423
         $ratinghtml = ''; //the string we'll return
1437 1424
 
1438  
-        //permissions check - can they view the aggregate?
1439  
-        $canviewaggregate = false;
  1425
+        // permissions check - can they view the aggregate?
  1426
+        if ($rating->user_can_view_aggregate()) {
1440 1427
 
1441  
-        //if its the current user's item and they have permission to view the aggregate on their own items
1442  
-        if ( $rating->itemuserid==$USER->id && $rating->settings->permissions->view && $rating->settings->pluginpermissions->view) {
1443  
-            $canviewaggregate = true;
1444  
-        }
  1428
+            $aggregatelabel = $ratingmanager->get_aggregate_label($rating->settings->aggregationmethod);
  1429
+            $aggregatestr   = $rating->get_aggregate_string();
1445 1430
 
1446  
-        //if the item doesnt belong to anyone or its another user's items and they can see the aggregate on items they don't own
1447  
-        //Note that viewany doesnt mean you can see the aggregate or ratings of your own items
1448  
-        if ( (empty($rating->itemuserid) or $rating->itemuserid!=$USER->id) && $rating->settings->permissions->viewany && $rating->settings->pluginpermissions->viewany ) {
1449  
-            $canviewaggregate = true;
1450  
-        }
1451  
-
1452  
-        if ($canviewaggregate==true) {
1453  
-            $aggregatelabel = '';
1454  
-            switch ($rating->settings->aggregationmethod) {
1455  
-                case RATING_AGGREGATE_AVERAGE :
1456  
-                    $aggregatelabel .= get_string("aggregateavg", "rating");
1457  
-                    break;
1458  
-                case RATING_AGGREGATE_COUNT :
1459  
-                    $aggregatelabel .= get_string("aggregatecount", "rating");
1460  
-                    break;
1461  
-                case RATING_AGGREGATE_MAXIMUM :
1462  
-                    $aggregatelabel .= get_string("aggregatemax", "rating");
1463  
-                    break;
1464  
-                case RATING_AGGREGATE_MINIMUM :
1465  
-                    $aggregatelabel .= get_string("aggregatemin", "rating");
1466  
-                    break;
1467  
-                case RATING_AGGREGATE_SUM :
1468  
-                    $aggregatelabel .= get_string("aggregatesum", "rating");
1469  
-                    break;
1470  
-            }
1471  
-            $aggregatelabel .= get_string('labelsep', 'langconfig');
1472  
-
1473  
-            //$scalemax = 0;//no longer displaying scale max
1474  
-            $aggregatestr = '';
1475  
-
1476  
-            //only display aggregate if aggregation method isn't COUNT
1477  
-            if ($rating->aggregate && $rating->settings->aggregationmethod!= RATING_AGGREGATE_COUNT) {
1478  
-                if ($rating->settings->aggregationmethod!= RATING_AGGREGATE_SUM && is_array($rating->settings->scale->scaleitems)) {
1479  
-                    $aggregatestr .= $rating->settings->scale->scaleitems[round($rating->aggregate)];//round aggregate as we're using it as an index
1480  
-                }
1481  
-                else { //aggregation is SUM or the scale is numeric
1482  
-                    $aggregatestr .= round($rating->aggregate,1);
1483  
-                }
  1431
+            $aggregatehtml  = html_writer::tag('span', $aggregatestr, array('id' => 'ratingaggregate'.$rating->itemid)).' ';
  1432
+            $aggregatehtml .= html_writer::start_tag('span', array('id'=>"ratingcount{$rating->itemid}"));
  1433
+            if ($rating->count > 0) {
  1434
+                $aggregatehtml .= "({$rating->count})";
1484 1435
             } else {
1485  
-                $aggregatestr = '';
  1436
+                $aggregatehtml .= '-';
1486 1437
             }
1487  
-
1488  
-            $countstr = html_writer::start_tag('span', array('id'=>"ratingcount{$rating->itemid}"));
1489  
-            if ($rating->count>0) {
1490  
-                $countstr .= "({$rating->count})";
1491  
-            }
1492  
-            $countstr .= html_writer::end_tag('span');
1493  
-
1494  
-            //$aggregatehtml = "{$ratingstr} / $scalemax ({$rating->count}) ";
1495  
-            $aggregatehtml = "<span id='ratingaggregate{$rating->itemid}'>{$aggregatestr}</span> $countstr ";
  1438
+            $aggregatehtml .= html_writer::end_tag('span').' ';
1496 1439
 
1497 1440
             $ratinghtml .= html_writer::tag('span', $aggregatelabel, array('class'=>'rating-aggregate-label'));
1498 1441
             if ($rating->settings->permissions->viewall && $rating->settings->pluginpermissions->viewall) {
1499  
-                $url = "/rating/index.php?contextid={$rating->context->id}&itemid={$rating->itemid}&scaleid={$rating->settings->scale->id}";
1500  
-                $nonpopuplink = new moodle_url($url);
1501  
-                $popuplink = new moodle_url("$url&popup=1");
  1442
+
  1443
+                $nonpopuplink = $rating->get_view_ratings_url();
  1444
+                $popuplink = $rating->get_view_ratings_url(true);
1502 1445
 
1503 1446
                 $action = new popup_action('click', $popuplink, 'ratings', array('height' => 400, 'width' => 600));
1504 1447
                 $ratinghtml .= $this->action_link($nonpopuplink, $aggregatehtml, $action);
@@ -1508,81 +1451,45 @@ function render_rating(rating $rating) {
1508 1451
         }
1509 1452
 
1510 1453
         $formstart = null;
1511  
-        //if the item doesn't belong to the current user, the user has permission to rate
1512  
-        //and we're within the assessable period
1513  
-        if ($rating->itemuserid!=$USER->id
1514  
-            && $rating->settings->permissions->rate
1515  
-            && $rating->settings->pluginpermissions->rate
1516  
-            && $inassessablewindow) {
1517  
-
1518  
-            //start the rating form
1519  
-            $formstart = html_writer::start_tag('form',
1520  
-                array('id'=>"postrating{$rating->itemid}", 'class'=>'postratingform', 'method'=>'post', 'action'=>"{$CFG->wwwroot}/rating/rate.php"));
1521  
-
1522  
-            $formstart .= html_writer::start_tag('div', array('class'=>'ratingform'));
1523  
-
1524  
-            //add the hidden inputs
1525  
-
1526  
-            $attributes = array('type'=>'hidden', 'class'=>'ratinginput', 'name'=>'contextid', 'value'=>$rating->context->id);
1527  
-            $formstart .= html_writer::empty_tag('input', $attributes);
1528  
-
1529  
-            $attributes['name'] = 'component';
1530  
-            $attributes['value'] = $rating->settings->component;
1531  
-            $formstart .= html_writer::empty_tag('input', $attributes);
1532  
-
1533  
-            $attributes['name'] = 'itemid';
1534  
-            $attributes['value'] = $rating->itemid;
1535  
-            $formstart .= html_writer::empty_tag('input', $attributes);
1536  
-
1537  
-            $attributes['name'] = 'scaleid';
1538  
-            $attributes['value'] = $rating->settings->scale->id;
1539  
-            $formstart .= html_writer::empty_tag('input', $attributes);
1540  
-
1541  
-            $attributes['name'] = 'returnurl';
1542  
-            $attributes['value'] = $rating->settings->returnurl;
1543  
-            $formstart .= html_writer::empty_tag('input', $attributes);
  1454
+        // if the item doesn't belong to the current user, the user has permission to rate
  1455
+        // and we're within the assessable period
  1456
+        if ($rating->user_can_rate()) {
1544 1457
 
1545  
-            $attributes['name'] = 'rateduserid';
1546  
-            $attributes['value'] = $rating->itemuserid;
1547  
-            $formstart .= html_writer::empty_tag('input', $attributes);
  1458
+            $rateurl = $rating->get_rate_url();
  1459
+            $inputs = $rateurl->params();
1548 1460
 
1549  
-            $attributes['name'] = 'aggregation';
1550  
-            $attributes['value'] = $rating->settings->aggregationmethod;
1551  
-            $formstart .= html_writer::empty_tag('input', $attributes);
1552  
-
1553  
-            $attributes['name'] = 'sesskey';
1554  
-            $attributes['value'] = sesskey();;
1555  
-            $formstart .= html_writer::empty_tag('input', $attributes);
  1461
+            //start the rating form
  1462
+            $formattrs = array(
  1463
+                'id'     => "postrating{$rating->itemid}",
  1464
+                'class'  => 'postratingform',
  1465
+                'method' => 'post',
  1466
+                'action' => $rateurl->out_omit_querystring()
  1467
+            );
  1468
+            $formstart  = html_writer::start_tag('form', $formattrs);
  1469
+            $formstart .= html_writer::start_tag('div', array('class' => 'ratingform'));
  1470
+
  1471
+            // add the hidden inputs
  1472
+            foreach ($inputs as $name => $value) {
  1473
+                $attributes = array('type' => 'hidden', 'class' => 'ratinginput', 'name' => $name, 'value' => $value);
  1474
+                $formstart .= html_writer::empty_tag('input', $attributes);
  1475
+            }
1556 1476
 
1557 1477
             if (empty($ratinghtml)) {
1558 1478
                 $ratinghtml .= $strrate.': ';
1559 1479
             }
1560  
-
1561 1480
             $ratinghtml = $formstart.$ratinghtml;
1562 1481
 
1563  
-            //generate an array of values for numeric scales
1564  
-            $scalearray = $rating->settings->scale->scaleitems;
1565  
-            if (!is_array($scalearray)) { //almost certainly a numerical scale
1566  
-                $intscalearray = intval($scalearray);//just in case they've passed "5" instead of 5
1567  
-                $scalearray = array();
1568  
-                if( is_int($intscalearray) && $intscalearray>0 ) {
1569  
-                    for($i=0; $i<=$rating->settings->scale->scaleitems; $i++) {
1570  
-                        $scalearray[$i] = $i;
1571  
-                    }
1572  
-                }
1573  
-            }
1574  
-
1575  
-            $scalearray = array(RATING_UNSET_RATING => $strrate.'...') + $scalearray;
1576  
-            $ratinghtml .= html_writer::select($scalearray, 'rating', $rating->rating, false, array('class'=>'postratingmenu ratinginput','id'=>'menurating'.$rating->itemid));
  1482
+            $scalearray = array(RATING_UNSET_RATING => $strrate.'...') + $rating->settings->scale->scaleitems;
  1483
+            $scaleattrs = array('class'=>'postratingmenu ratinginput','id'=>'menurating'.$rating->itemid);
  1484
+            $ratinghtml .= html_writer::select($scalearray, 'rating', $rating->rating, false, $scaleattrs);
1577 1485
 
1578 1486
             //output submit button
1579  
-
1580 1487
             $ratinghtml .= html_writer::start_tag('span', array('class'=>"ratingsubmit"));
1581 1488
 
1582  
-            $attributes = array('type'=>'submit', 'class'=>'postratingmenusubmit', 'id'=>'postratingsubmit'.$rating->itemid, 'value'=>s(get_string('rate', 'rating')));
  1489
+            $attributes = array('type' => 'submit', 'class' => 'postratingmenusubmit', 'id' => 'postratingsubmit'.$rating->itemid, 'value' => s(get_string('rate', 'rating')));
1583 1490
             $ratinghtml .= html_writer::empty_tag('input', $attributes);
1584 1491
 
1585  
-            if (is_array($rating->settings->scale->scaleitems)) {
  1492
+            if (!$rating->settings->scale->isnumeric) {
1586 1493
                 $ratinghtml .= $this->help_icon_scale($rating->settings->scale->courseid, $rating->settings->scale);
1587 1494
             }
1588 1495
             $ratinghtml .= html_writer::end_tag('span');
@@ -2424,7 +2331,7 @@ protected function render_navigation_node(navigation_node $item) {
2424 2331
             $content = $icon.$content; // use CSS for spacing of icons
2425 2332
         }
2426 2333
         if ($item->helpbutton !== null) {
2427  
-            $content = trim($item->helpbutton).html_writer::tag('span', $content, array('class'=>'clearhelpbutton'));
  2334
+            $content = trim($item->helpbutton).html_writer::tag('span', $content, array('class'=>'clearhelpbutton', 'tabindex'=>'0'));
2428 2335
         }
2429 2336
         if ($content === '') {
2430 2337
             return '';
@@ -2447,7 +2354,7 @@ protected function render_navigation_node(navigation_node $item) {
2447 2354
             $content = html_writer::link($item->action, $content, $attributes);
2448 2355
 
2449 2356
         } else if (is_string($item->action) || empty($item->action)) {
2450  
-            $attributes = array();
  2357
+            $attributes = array('tabindex'=>'0'); //add tab support to span but still maintain character stream sequence.
2451 2358
             if ($title !== '') {
2452 2359
                 $attributes['title'] = $title;
2453 2360
             }
2  lib/rsslib.php
@@ -280,7 +280,7 @@ function rss_add_items($items) {
280 280
             $result .= rss_add_enclosures($item);
281 281
             $result .= rss_full_tag('pubDate',3,false,gmdate('D, d M Y H:i:s',$item->pubdate).' GMT');  # MDL-12563
282 282
             //Include the author if exists
283  
-            if (isset($item->author)) {
  283
+            if (isset($item->author) && !empty($item->author)) {
284 284
                 //$result .= rss_full_tag('author',3,false,$item->author);
285 285
                 //We put it in the description instead because it's more important
286 286
                 //for moodle than most other feeds, and most rss software seems to ignore
2  message/lib.php
@@ -1966,6 +1966,8 @@ function message_post_message($userfrom, $userto, $message, $format) {
1966 1966
  * Returns a list of all user ids who have used messaging in the site
1967 1967
  * This was the simple way to code the SQL ... is it going to blow up
1968 1968
  * on large datasets?
  1969
+ *
  1970
+ * @todo: deprecated - to be deleted in 2.2
1969 1971
  */
1970 1972
 function message_get_participants() {
1971 1973
     global $CFG, $DB;
2  mod/assignment/lib.php
@@ -2772,6 +2772,8 @@ function assignment_grade_item_delete($assignment) {
2772 2772
 /**
2773 2773
  * Returns the users with data in one assignment (students and teachers)
2774 2774
  *
  2775
+ * @todo: deprecated - to be deleted in 2.2
  2776
+ *
2775 2777
  * @param $assignmentid int
2776 2778
  * @return array of user objects
2777 2779
  */
3  mod/chat/lib.php
@@ -430,7 +430,8 @@ function chat_cron () {
430 430
  * Returns the users with data in one chat
431 431
  * (users with records in chat_messages, students)
432 432
  *
433  
- * @global object
  433
+ * @todo: deprecated - to be deleted in 2.2
  434
+ *
434 435
  * @param int $chatid
435 436
  * @param int $groupid
436 437
  * @return array
2  mod/choice/lib.php
@@ -591,6 +591,8 @@ function choice_delete_instance($id) {
591 591
  * Returns the users with data in one choice
592 592
  * (users with records in choice_responses, students)
593 593
  *
  594
+ * @todo: deprecated - to be deleted in 2.2
  595
+ *
594 596
  * @param int $choiceid
595 597
  * @return array
596 598
  */
2  mod/choice/renderer.php
@@ -40,7 +40,7 @@ public function display_options($options, $coursemoduleid, $vertical = false) {
40 40
             $layoutclass = 'vertical';
41 41
         }
42 42
         $target = new moodle_url('/mod/choice/view.php');
43  
-        $attributes = array('method'=>'POST', 'target'=>$target, 'class'=> $layoutclass);
  43
+        $attributes = array('method'=>'POST', 'action'=>$target, 'class'=> $layoutclass);
44 44
 
45 45
         $html = html_writer::start_tag('form', $attributes);
46 46
         $html .= html_writer::start_tag('ul', array('class'=>'choices' ));
8  mod/data/backup/moodle2/backup_data_stepslib.php
@@ -69,7 +69,7 @@ protected function define_structure() {
69 69
         $ratings = new backup_nested_element('ratings');
70 70
 
71 71
         $rating = new backup_nested_element('rating', array('id'), array(
72  
-            'scaleid', 'value', 'userid', 'timecreated', 'timemodified'));
  72
+            'component', 'ratingarea', 'scaleid', 'value', 'userid', 'timecreated', 'timemodified'));
73 73
 
74 74
         // Build the tree
75 75
         $data->add_child($fields);
@@ -99,8 +99,10 @@ protected function define_structure() {
99 99
 
100 100
             $content->set_source_table('data_content', array('recordid' => backup::VAR_PARENTID));
101 101
 
102  
-            $rating->set_source_table('rating', array('contextid' => backup::VAR_CONTEXTID,
103  
-                                                      'itemid'    => backup::VAR_PARENTID));
  102
+            $rating->set_source_table('rating', array('contextid'  => backup::VAR_CONTEXTID,
  103
+                                                      'itemid'     => backup::VAR_PARENTID,
  104
+                                                      'component'  => 'mod_data',
  105
+                                                      'ratingarea' => 'entry'));
104 106
             $rating->set_source_alias('rating', 'value');
105 107
         }
106 108
 
8  mod/data/backup/moodle2/restore_data_stepslib.php
@@ -138,6 +138,14 @@ protected function process_data_rating($data) {
138 138
         $data->timecreated = $this->apply_date_offset($data->timecreated);
139 139
         $data->timemodified = $this->apply_date_offset($data->timemodified);
140 140
 
  141
+        // We need to check that component and ratingarea are both set here.
  142
+        if (empty($data->component)) {
  143
+            $data->component = 'mod_data';
  144
+        }