Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

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
@mudrd8mz mudrd8mz authored
Showing with 3,511 additions and 930 deletions.
  1. +34 −0 backup/moodle2/backup_coursereport_plugin.class.php
  2. +1 −0  backup/moodle2/backup_plan_builder.class.php
  3. +4 −0 backup/moodle2/backup_stepslib.php
  4. +29 −0 backup/moodle2/restore_coursereport_plugin.class.php
  5. +2 −0  backup/moodle2/restore_plan_builder.class.php
  6. +3 −0  backup/moodle2/restore_stepslib.php
  7. +2 −2 backup/util/xml/parser/processors/simplified_parser_processor.class.php
  8. +16 −14 backup/util/xml/parser/simpletest/testparser.php
  9. +88 −4 blocks/dock.js
  10. +1 −0  blocks/navigation/renderer.php
  11. +108 −8 blocks/navigation/yui/navigation/navigation.js
  12. +1 −0  lang/en/error.php
  13. +7 −5 lib/db/install.xml
  14. +50 −0 lib/db/upgrade.php
  15. +44 −137 lib/outputrenderers.php
  16. +1 −1  lib/rsslib.php
  17. +2 −0  message/lib.php
  18. +2 −0  mod/assignment/lib.php
  19. +2 −1  mod/chat/lib.php
  20. +2 −0  mod/choice/lib.php
  21. +1 −1  mod/choice/renderer.php
  22. +5 −3 mod/data/backup/moodle2/backup_data_stepslib.php
  23. +8 −0 mod/data/backup/moodle2/restore_data_stepslib.php
  24. +23 −0 mod/data/db/upgrade.php
  25. +86 −61 mod/data/lib.php
  26. +2 −2 mod/data/version.php
  27. +3 −2 mod/data/view.php
  28. +1 −0  mod/feedback/lib.php
  29. +2 −0  mod/folder/lib.php
  30. +5 −3 mod/forum/backup/moodle2/backup_forum_stepslib.php
  31. +8 −0 mod/forum/backup/moodle2/restore_forum_stepslib.php
  32. +22 −0 mod/forum/db/upgrade.php
  33. +147 −153 mod/forum/lib.php
  34. +30 −39 mod/forum/user.php
  35. +3 −5 mod/forum/version.php
  36. +0 −4 mod/forum/view.php
  37. +5 −3 mod/glossary/backup/moodle2/backup_glossary_stepslib.php
  38. +9 −0 mod/glossary/backup/moodle2/restore_glossary_stepslib.php
  39. +23 −0 mod/glossary/db/upgrade.php
  40. +3 −1 mod/glossary/deleteentry.php
  41. +91 −62 mod/glossary/lib.php
  42. +2 −2 mod/glossary/version.php
  43. +3 −3 mod/glossary/view.php
  44. +2 −0  mod/imscp/lib.php
  45. +2 −0  mod/label/lib.php
  46. +2 −2 mod/lesson/lib.php
  47. +2 −0  mod/page/lib.php
  48. +2 −2 mod/quiz/lib.php
  49. +2 −0  mod/resource/lib.php
  50. +2 −1  mod/survey/lib.php
  51. +2 −0  mod/url/lib.php
  52. +2 −0  mod/wiki/lib.php
  53. +2 −0  mod/workshop/lib.php
  54. +36 −31 rating/index.php
  55. +696 −301 rating/lib.php
  56. +27 −24 rating/rate.php
  57. +45 −48 rating/rate_ajax.php
  58. +3 −3 repository/webdav/lib.php
  59. +133 −0 theme/afterburner/config.php
  60. +55 −0 theme/afterburner/lang/en/theme_afterburner.php
  61. +128 −0 theme/afterburner/layout/default.php
  62. +23 −0 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 −0  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 −0 theme/afterburner/renderers.php
  257. +155 −0 theme/afterburner/style/afterburner_blocks.css
  258. +167 −0 theme/afterburner/style/afterburner_calendar.css
  259. +125 −0 theme/afterburner/style/afterburner_dock.css
  260. +148 −0 theme/afterburner/style/afterburner_layout.css
  261. +193 −0 theme/afterburner/style/afterburner_menu.css
  262. +86 −0 theme/afterburner/style/afterburner_mod.css
  263. +436 −0 theme/afterburner/style/afterburner_styles.css
  264. +7 −0 theme/afterburner/style/rtl.css
  265. +5 −0 theme/base/style/core.css
  266. +2 −2 version.php
View
34 backup/moodle2/backup_coursereport_plugin.class.php
@@ -0,0 +1,34 @@
+<?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/>.
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Base class for course report backup plugins.
+ *
+ * NOTE: When you back up a course, it potentially may run backup for all
+ * course reports. In order to control whether a particular report gets
+ * backed up, a course report should make use of the second and third
+ * parameters in get_plugin_element().
+ *
+ * @package moodlecore
+ * @subpackage backup-moodle2
+ * @copyright 2011 onwards The Open University
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+abstract class backup_coursereport_plugin extends backup_plugin {
+ // Use default parent behaviour
+}
View
1  backup/moodle2/backup_plan_builder.class.php
@@ -36,6 +36,7 @@
require_once($CFG->dirroot . '/backup/moodle2/backup_qtype_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/backup_format_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/backup_theme_plugin.class.php');
+require_once($CFG->dirroot . '/backup/moodle2/backup_coursereport_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/backup_plagiarism_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/backup_subplugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/backup_settingslib.php');
View
4 backup/moodle2/backup_stepslib.php
@@ -419,6 +419,10 @@ protected function define_structure() {
// save course data (in case of user theme, legacy theme, etc)
$this->add_plugin_structure('theme', $course, true);
+ // attach course report plugin structure to $course element; multiple
+ // course reports can save course data if required
+ $this->add_plugin_structure('coursereport', $course, true);
+
// attach plagiarism plugin structure to $course element, only one allowed
$this->add_plugin_structure('plagiarism', $course, false);
View
29 backup/moodle2/restore_coursereport_plugin.class.php
@@ -0,0 +1,29 @@
+<?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/>.
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Restore for course plugin: course report.
+ *
+ * @package moodlecore
+ * @subpackage backup-moodle2
+ * @copyright 2011 The Open University
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+abstract class restore_coursereport_plugin extends restore_plugin {
+ // Use default parent behaviour
+}
View
2  backup/moodle2/restore_plan_builder.class.php
@@ -35,11 +35,13 @@
require_once($CFG->dirroot . '/backup/moodle2/restore_qtype_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/restore_format_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/restore_theme_plugin.class.php');
+require_once($CFG->dirroot . '/backup/moodle2/restore_coursereport_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/restore_plagiarism_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/backup_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/backup_qtype_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/backup_format_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/backup_theme_plugin.class.php');
+require_once($CFG->dirroot . '/backup/moodle2/backup_coursereport_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/backup_plagiarism_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/restore_subplugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/restore_settingslib.php');
View
3  backup/moodle2/restore_stepslib.php
@@ -1010,6 +1010,9 @@ protected function define_structure() {
// Apply for 'theme' plugins optional paths at course level
$this->add_plugin_structure('theme', $course);
+ // Apply for 'course report' plugins optional paths at course level
+ $this->add_plugin_structure('coursereport', $course);
+
// Apply for plagiarism plugins optional paths at course level
$this->add_plugin_structure('plagiarism', $course);
View
4 backup/util/xml/parser/processors/simplified_parser_processor.class.php
@@ -174,12 +174,12 @@ public function before_path($path) {
*/
public function after_path($path) {
$toprocess = false;
- // If the path being closed matches (same or parent) the last path in the stack
+ // If the path being closed matches (same or parent) the first path in the stack
// we process pending startend notifications until one matching end is found
if ($element = reset($this->startendinfo)) {
$elepath = $element['path'];
$eleaction = $element['action'];
- if ($eleaction = 'end' && strpos($elepath, $path) === 0) {
+ if (strpos($elepath, $path) === 0) {
$toprocess = true;
}
View
30 backup/util/xml/parser/simpletest/testparser.php
@@ -652,43 +652,45 @@ function test_grouped_david_backup19_file_fragment() {
function helper_check_notifications_order_integrity($notifications) {
$numerrors = 0;
$notifpile = array('pilebase' => 'start');
- $lastpile = 'start:pilebase';
+ $lastnotif = 'start:pilebase';
foreach ($notifications as $notif) {
- $lastpilelevel = strlen(preg_replace('/[^\/]/', '', $lastpile));
- $lastpiletype = preg_replace('/:.*/', '', $lastpile);
- $lastpilepath = preg_replace('/.*:/', '', $lastpile);
- $notiflevel = strlen(preg_replace('/[^\/]/', '', $notif));
+ $lastpiletype = end($notifpile);
+ $lastpilepath = key($notifpile);
+ $lastpilelevel = strlen(preg_replace('/[^\/]/', '', $lastpilepath));
+
+ $lastnotiftype = preg_replace('/:.*/', '', $lastnotif);
+ $lastnotifpath = preg_replace('/.*:/', '', $lastnotif);
+ $lastnotiflevel = strlen(preg_replace('/[^\/]/', '', $lastnotifpath));
+
$notiftype = preg_replace('/:.*/', '', $notif);
$notifpath = preg_replace('/.*:/', '', $notif);
+ $notiflevel = strlen(preg_replace('/[^\/]/', '', $notifpath));
switch ($notiftype) {
case 'process':
- if ($lastpilepath != $notifpath or $lastpiletype != 'start') {
- $numerrors++; // Only start for same path is allowed before process
+ if ($lastnotifpath != $notifpath or $lastnotiftype != 'start') {
+ $numerrors++; // Only start for same path from last notification is allowed before process
}
$notifpile[$notifpath] = 'process'; // Update the status in the pile
break;
case 'end':
if ($lastpilepath != $notifpath or ($lastpiletype != 'process' and $lastpiletype != 'start')) {
- $numerrors++; // Only process for same path is allowed before end
+ $numerrors++; // Only process and start for same path from last pile is allowed before end
}
unset($notifpile[$notifpath]); // Delete from the pile
break;
case 'start':
if (array_key_exists($notifpath, $notifpile) or $notiflevel <= $lastpilelevel) {
- $numerrors++; // If same path exists or the level is < than the last one
+ $numerrors++; // Only non existing in pile and with level > last pile is allowed on start
}
$notifpile[$notifpath] = 'start'; // Add to the pile
break;
default:
$numerrors++; // Incorrect type of notification => error
}
- // Update lastpile
- end($notifpile);
- $path = key($notifpile);
- $type = $notifpile[$path];
- $lastpile = $type. ':' . $path;
+ // Update lastnotif
+ $lastnotif = $notif;
}
return $numerrors;
}
View
92 blocks/dock.js
@@ -66,7 +66,75 @@ M.core_dock.init = function(Y) {
// Give the dock item class the event properties/methods
Y.augment(this.item, Y.EventTarget);
Y.augment(this, Y.EventTarget, true);
+ /**
+ * A 'dock:actionkey' Event.
+ * The event consists of the left arrow, right arrow, enter and space keys.
+ * More keys can be mapped to action meanings.
+ * actions: collapse , expand, toggle, enter.
+ *
+ * This event is subscribed to by dockitems.
+ * The on() method to subscribe allows specifying the desired trigger actions as JSON.
+ *
+ * This event can also be delegated if needed.
+ * Todo: This could be centralised, a similar Event is defined in blocks/navigation/yui/navigation/navigation.js
+ */
+ Y.Event.define("dock:actionkey", {
+ // Webkit and IE repeat keydown when you hold down arrow keys.
+ // Opera links keypress to page scroll; others keydown.
+ // Firefox prevents page scroll via preventDefault() on either
+ // keydown or keypress.
+ _event: (Y.UA.webkit || Y.UA.ie) ? 'keydown' : 'keypress',
+
+ _keys: {
+ //arrows
+ '37': 'collapse',
+ '39': 'expand',
+ //(@todo: lrt/rtl/M.core_dock.cfg.orientation decision to assign arrow to meanings)
+ '32': 'toggle',
+ '13': 'enter'
+ },
+
+ _keyHandler: function (e, notifier, args) {
+ if (!args.actions) {
+ var actObj = {collapse:true, expand:true, toggle:true, enter:true};
+ } else {
+ var actObj = args.actions;
+ }
+ if (this._keys[e.keyCode] && actObj[this._keys[e.keyCode]]) {
+ e.action = this._keys[e.keyCode];
+ notifier.fire(e);
+ }
+ },
+
+ on: function (node, sub, notifier) {
+ // subscribe to _event and ask keyHandler to handle with given args[0] (the desired actions).
+ if (sub.args == null) {
+ //no actions given
+ sub._detacher = node.on(this._event, this._keyHandler,this, notifier, {actions:false});
+ } else {
+ sub._detacher = node.on(this._event, this._keyHandler,this, notifier, sub.args[0]);
+ }
+ },
+ detach: function (node, sub, notifier) {
+ //detach our _detacher handle of the subscription made in on()
+ sub._detacher.detach();
+ },
+
+ delegate: function (node, sub, notifier, filter) {
+ // subscribe to _event and ask keyHandler to handle with given args[0] (the desired actions).
+ if (sub.args == null) {
+ //no actions given
+ sub._delegateDetacher = node.delegate(this._event, this._keyHandler,filter, this, notifier, {actions:false});
+ } else {
+ sub._delegateDetacher = node.delegate(this._event, this._keyHandler,filter, this, notifier, sub.args[0]);
+ }
+ },
+
+ detachDelegate: function (node, sub, notifier) {
+ sub._delegateDetacher.detach();
+ }
+ });
// Publish the events the dock has
this.publish('dock:beforedraw', {prefix:'dock'});
this.publish('dock:beforeshow', {prefix:'dock'});
@@ -125,9 +193,10 @@ M.core_dock.init = function(Y) {
// Add a removeall button
// Must set the image src seperatly of we get an error with XML strict headers
- var removeall = Y.Node.create('<img alt="'+M.str.block.undockall+'" title="'+M.str.block.undockall+'" />');
+ var removeall = Y.Node.create('<img alt="'+M.str.block.undockall+'" title="'+M.str.block.undockall+'" tabindex="0"/>');
removeall.setAttribute('src',this.cfg.removeallicon);
removeall.on('removeall|click', this.remove_all, this);
+ removeall.on('dock:actionkey', this.remove_all, this, {actions:{enter:true}});
this.nodes.buttons.appendChild(Y.Node.create('<div class="'+css.controls+'"></div>').append(removeall));
// 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() {
* @function
* @return {boolean}
*/
-M.core_dock.remove_all = function() {
+M.core_dock.remove_all = function(e) {
for (var i in this.items) {
this.remove(i);
}
@@ -840,9 +909,10 @@ M.core_dock.genericblock.prototype = {
}, this);
// Add a close icon
// Must set the image src seperatly of we get an error with XML strict headers
- var closeicon = Y.Node.create('<span class="hidepanelicon"><img alt="" style="width:11px;height:11px;cursor:pointer;" /></span>');
+ var closeicon = Y.Node.create('<span class="hidepanelicon" tabindex="0"><img alt="" style="width:11px;height:11px;cursor:pointer;" /></span>');
closeicon.one('img').setAttribute('src', M.util.image_url('t/dockclose', 'moodle'));
closeicon.on('forceclose|click', this.hide, this);
+ closeicon.on('dock:actionkey',this.hide, this, {actions:{enter:true,toggle:true}});
this.commands.append(closeicon);
}, dockitem);
// 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 = {
this.nodes.docktitle = Y.Node.create('<div id="dock_item_'+this.id+'_title" class="'+css.dockedtitle+'"></div>');
this.nodes.docktitle.append(this.title);
- this.nodes.dockitem = Y.Node.create('<div id="dock_item_'+this.id+'" class="'+css.dockeditem+'"></div>');
+ this.nodes.dockitem = Y.Node.create('<div id="dock_item_'+this.id+'" class="'+css.dockeditem+'" tabindex="0"></div>');
+ this.nodes.dockitem.on('dock:actionkey', this.toggle, this);
if (M.core_dock.count === 1) {
this.nodes.dockitem.addClass('firstdockitem');
}
@@ -1000,6 +1071,19 @@ M.core_dock.item.prototype = {
this.fire('dockeditem:hidecomplete');
},
/**
+ * A toggle between calling show and hide functions based on css.activeitem
+ * Applies rules to key press events (dock:actionkey)
+ * @param {Event} e
+ */
+ toggle : function(e) {
+ var css = M.core_dock.css;
+ if (this.nodes.docktitle.hasClass(css.activeitem) && !(e.type == 'dock:actionkey' && e.action=='expand')) {
+ this.hide();
+ } else if (!this.nodes.docktitle.hasClass(css.activeitem) && !(e.type == 'dock:actionkey' && e.action=='collapse')) {
+ this.show();
+ }
+ },
+ /**
* This function removes the node and destroys it's bits
* @param {Event} e
*/
View
1  blocks/navigation/renderer.php
@@ -52,6 +52,7 @@ protected function navigation_node($items, $attrs=array(), $expansionlimit=null,
$attributes['class'] = 'dimmed_text';
}
if (is_string($item->action) || empty($item->action) || ($item->type === navigation_node::TYPE_CATEGORY && empty($options['linkcategories']))) {
+ $attributes['tabindex'] = '0'; //add tab support to span but still maintain character stream sequence.
$content = html_writer::tag('span', $content, $attributes);
} else if ($item->action instanceof action_link) {
//TODO: to be replaced with something else
View
116 blocks/navigation/yui/navigation/navigation.js
@@ -1,5 +1,74 @@
YUI.add('moodle-block_navigation-navigation', function(Y){
+/**
+ * A 'actionkey' Event to help with Y.delegate().
+ * The event consists of the left arrow, right arrow, enter and space keys.
+ * More keys can be mapped to action meanings.
+ * actions: collapse , expand, toggle, enter.
+ *
+ * This event is delegated to branches in the navigation tree.
+ * The on() method to subscribe allows specifying the desired trigger actions as JSON.
+ *
+ * Todo: This could be centralised, a similar Event is defined in blocks/dock.js
+ */
+Y.Event.define("actionkey", {
+ // Webkit and IE repeat keydown when you hold down arrow keys.
+ // Opera links keypress to page scroll; others keydown.
+ // Firefox prevents page scroll via preventDefault() on either
+ // keydown or keypress.
+ _event: (Y.UA.webkit || Y.UA.ie) ? 'keydown' : 'keypress',
+
+ _keys: {
+ //arrows
+ '37': 'collapse',
+ '39': 'expand',
+ //(@todo: lrt/rtl/M.core_dock.cfg.orientation decision to assign arrow to meanings)
+ '32': 'toggle',
+ '13': 'enter'
+ },
+
+ _keyHandler: function (e, notifier, args) {
+ if (!args.actions) {
+ var actObj = {collapse:true, expand:true, toggle:true, enter:true};
+ } else {
+ var actObj = args.actions;
+ }
+ if (this._keys[e.keyCode] && actObj[this._keys[e.keyCode]]) {
+ e.action = this._keys[e.keyCode];
+ notifier.fire(e);
+ }
+ },
+
+ on: function (node, sub, notifier) {
+ // subscribe to _event and ask keyHandler to handle with given args[0] (the desired actions).
+ if (sub.args == null) {
+ //no actions given
+ sub._detacher = node.on(this._event, this._keyHandler,this, notifier, {actions:false});
+ } else {
+ sub._detacher = node.on(this._event, this._keyHandler,this, notifier, sub.args[0]);
+ }
+ },
+
+ detach: function (node, sub, notifier) {
+ //detach our _detacher handle of the subscription made in on()
+ sub._detacher.detach();
+ },
+
+ delegate: function (node, sub, notifier, filter) {
+ // subscribe to _event and ask keyHandler to handle with given args[0] (the desired actions).
+ if (sub.args == null) {
+ //no actions given
+ sub._delegateDetacher = node.delegate(this._event, this._keyHandler,filter, this, notifier, {actions:false});
+ } else {
+ sub._delegateDetacher = node.delegate(this._event, this._keyHandler,filter, this, notifier, sub.args[0]);
+ }
+ },
+
+ detachDelegate: function (node, sub, notifier) {
+ sub._delegateDetacher.detach();
+ }
+});
+
var EXPANSIONLIMIT_EVERYTHING = 0,
EXPANSIONLIMIT_COURSE = 20,
EXPANSIONLIMIT_SECTION = 30,
@@ -36,6 +105,7 @@ TREE.prototype = {
// Delegate event to toggle expansion
var self = this;
Y.delegate('click', function(e){self.toggleExpansion(e);}, node.one('.block_tree'), '.tree_item.branch');
+ Y.delegate('actionkey', function(e){self.toggleExpansion(e);}, node.one('.block_tree'), '.tree_item.branch');
// Gather the expandable branches ready for initialisation.
var expansions = [];
@@ -71,8 +141,8 @@ TREE.prototype = {
// First check if they managed to click on the li iteslf, then find the closest
// LI ancestor and use that
- if (e.target.test('a')) {
- // A link has been clicked don't fire any more events just do the default.
+ if (e.target.test('a') && (e.keyCode == 0 || e.keyCode == 13)) {
+ // A link has been clicked (or keypress is 'enter') don't fire any more events just do the default.
e.stopPropagation();
return;
}
@@ -88,7 +158,21 @@ TREE.prototype = {
// Toggle expand/collapse providing its not a root level branch.
if (!target.hasClass('depth_1')) {
- target.toggleClass('collapsed');
+ if (e.type == 'actionkey') {
+ switch (e.action) {
+ case 'expand' :
+ target.removeClass('collapsed');
+ break;
+ case 'collapse' :
+ target.addClass('collapsed');
+ break;
+ default :
+ target.toggleClass('collapsed');
+ }
+ e.halt();
+ } else {
+ target.toggleClass('collapsed');
+ }
}
// If the accordian feature has been enabled collapse all siblings.
@@ -152,9 +236,10 @@ BRANCH.prototype = {
*/
node : null,
/**
- * A reference to the ajax load event handle when created.
+ * A reference to the ajax load event handlers when created.
*/
event_ajaxload : null,
+ event_ajaxload_actionkey : null,
/**
* Initialises the branch when it is first created.
*/
@@ -193,8 +278,13 @@ BRANCH.prototype = {
var isbranch = (this.get('expandable') || this.get('haschildren'));
var branchli = Y.Node.create('<li></li>');
+ var link = this.get('link');
var branchp = Y.Node.create('<p class="tree_item"></p>').setAttribute('id', this.get('id'));
-
+ if (!link) {
+ //add tab focus if not link (so still one focus per menu node).
+ // it was suggested to have 2 foci. one for the node and one for the link in MDL-27428.
+ branchp.setAttribute('tabindex', '0');
+ }
if (isbranch) {
branchli.addClass('collapsed').addClass('contains_branch');
branchp.addClass('branch');
@@ -220,7 +310,6 @@ BRANCH.prototype = {
}
}
- var link = this.get('link');
if (!link) {
if (branchicon) {
branchp.appendChild(branchicon);
@@ -253,6 +342,7 @@ BRANCH.prototype = {
}
if (this.get('expandable')) {
this.event_ajaxload = this.node.on('ajaxload|click', this.ajaxLoad, this);
+ this.event_ajaxload_actionkey = this.node.on('actionkey', this.ajaxLoad, this);
}
return this;
},
@@ -274,7 +364,16 @@ BRANCH.prototype = {
* request made here.
*/
ajaxLoad : function(e) {
- e.stopPropagation();
+ if (e.type == 'actionkey' && e.action != 'enter') {
+ e.halt();
+ } else {
+ e.stopPropagation();
+ }
+ if (e.type = 'actionkey' && e.action == 'enter' && e.target.test('A')) {
+ this.event_ajaxload_actionkey.detach();
+ this.event_ajaxload.detach();
+ return true; // no ajaxLoad for enter
+ }
if (this.node.hasClass('loadingbranch')) {
return true;
@@ -307,6 +406,7 @@ BRANCH.prototype = {
ajaxProcessResponse : function(tid, outcome) {
this.node.removeClass('loadingbranch');
this.event_ajaxload.detach();
+ this.event_ajaxload_actionkey.detach();
try {
var object = Y.JSON.parse(outcome.responseText);
if (object.children && object.children.length > 0) {
@@ -455,4 +555,4 @@ M.block_navigation = M.block_navigation || {
}
};
-}, '@VERSION@', {requires:['base', 'core_dock', 'io', 'node', 'dom', 'event-custom', 'event-delegate', 'json-parse']});
+}, '@VERSION@', {requires:['base', 'core_dock', 'io', 'node', 'dom', 'event-custom', 'event-delegate', 'json-parse']});
View
1  lang/en/error.php
@@ -303,6 +303,7 @@
$string['invalidpasswordpolicy'] = 'Invalid password policy';
$string['invalidpaymentmethod'] = 'Invalid payment method: {$a}';
$string['invalidqueryparam'] = 'ERROR: Incorrect number of query parameters. Expected {$a->expected}, got {$a->actual}.';
+$string['invalidratingarea'] = 'Invalid rating area';
$string['invalidrecord'] = 'Can not find data record in database table {$a}.';
$string['invalidrecordunknown'] = 'Can not find data record in database.';
$string['invalidrequest'] = 'Invalid request';
View
12 lib/db/install.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<XMLDB PATH="lib/db" VERSION="20110209" COMMENT="XMLDB file for core Moodle tables"
+<XMLDB PATH="lib/db" VERSION="20110523" COMMENT="XMLDB file for core Moodle tables"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../lib/xmldb/xmldb.xsd"
>
@@ -2591,8 +2591,10 @@
<TABLE NAME="rating" COMMENT="moodle ratings" PREVIOUS="blog_external" NEXT="license">
<FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="true" NEXT="contextid"/>
- <FIELD NAME="contextid" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="false" PREVIOUS="id" NEXT="itemid"/>
- <FIELD NAME="itemid" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="false" PREVIOUS="contextid" NEXT="scaleid"/>
+ <FIELD NAME="contextid" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="false" PREVIOUS="id" NEXT="component"/>
+ <FIELD NAME="component" TYPE="char" LENGTH="100" NOTNULL="true" SEQUENCE="false" PREVIOUS="contextid" NEXT="ratingarea"/>
+ <FIELD NAME="ratingarea" TYPE="char" LENGTH="50" NOTNULL="true" SEQUENCE="false" PREVIOUS="component" NEXT="itemid"/>
+ <FIELD NAME="itemid" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="false" PREVIOUS="ratingarea" NEXT="scaleid"/>
<FIELD NAME="scaleid" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="false" SEQUENCE="false" PREVIOUS="itemid" NEXT="rating"/>
<FIELD NAME="rating" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="false" PREVIOUS="scaleid" NEXT="userid"/>
<FIELD NAME="userid" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="false" PREVIOUS="rating" NEXT="timecreated"/>
@@ -2605,7 +2607,7 @@
<KEY NAME="userid" TYPE="foreign" FIELDS="userid" REFTABLE="user" REFFIELDS="id" COMMENT="Relates to user.id" PREVIOUS="contextid"/>
</KEYS>
<INDEXES>
- <INDEX NAME="itemid" UNIQUE="false" FIELDS="itemid"/>
+ <INDEX NAME="uniqueuserrating" UNIQUE="false" FIELDS="component, ratingarea, contextid, itemid" COMMENT="These fields define a unique user rating of an item"/>
</INDEXES>
</TABLE>
<TABLE NAME="license" COMMENT="store licenses used by moodle" PREVIOUS="rating" NEXT="registration_hubs">
@@ -2753,4 +2755,4 @@
</KEYS>
</TABLE>
</TABLES>
-</XMLDB>
+</XMLDB>
View
50 lib/db/upgrade.php
@@ -6062,6 +6062,56 @@ function xmldb_main_upgrade($oldversion) {
upgrade_main_savepoint(true, 2011022100.01);
}
+ if ($oldversion < 2011052300.00) {
+ $table = new xmldb_table('rating');
+
+ // Add the component field to the ratings table
+ upgrade_set_timeout(60 * 20);
+ $field = new xmldb_field('component', XMLDB_TYPE_CHAR, '100', null, XMLDB_NOTNULL, null, 'unknown', 'contextid');
+ if (!$dbman->field_exists($table, $field)) {
+ $dbman->add_field($table, $field);
+ }
+
+ // Add the ratingarea field to the ratings table
+ upgrade_set_timeout(60 * 20);
+ $field = new xmldb_field('ratingarea', XMLDB_TYPE_CHAR, '50', null, XMLDB_NOTNULL, null, 'unknown', 'component');
+ if (!$dbman->field_exists($table, $field)) {
+ $dbman->add_field($table, $field);
+ }
+
+ upgrade_main_savepoint(true, 2011052300.00);
+ }
+
+ if ($oldversion < 2011052300.01) {
+
+ // Define index uniqueuserrating (unique) to be added to rating
+ $table = new xmldb_table('rating');
+ $index = new xmldb_index('uniqueuserrating', XMLDB_INDEX_NOTUNIQUE, array('component', 'ratingarea', 'contextid', 'itemid'));
+
+ // Conditionally launch add index uniqueuserrating
+ if (!$dbman->index_exists($table, $index)) {
+ $dbman->add_index($table, $index);
+ }
+
+ // Main savepoint reached
+ upgrade_main_savepoint(true, 2011052300.01);
+ }
+
+ if ($oldversion < 2011052300.02) {
+
+ // Define index itemid (not unique) to be dropped form rating
+ $table = new xmldb_table('rating');
+ $index = new xmldb_index('itemid', XMLDB_INDEX_NOTUNIQUE, array('itemid'));
+
+ // Conditionally launch drop index itemid
+ if ($dbman->index_exists($table, $index)) {
+ $dbman->drop_index($table, $index);
+ }
+
+ // Main savepoint reached
+ upgrade_main_savepoint(true, 2011052300.02);
+ }
+
return true;
}
View
181 lib/outputrenderers.php
@@ -1410,95 +1410,38 @@ protected function render_pix_emoticon(pix_emoticon $emoticon) {
*/
function render_rating(rating $rating) {
global $CFG, $USER;
- static $havesetupjavascript = false;
- if( $rating->settings->aggregationmethod == RATING_AGGREGATE_NONE ){
+ if ($rating->settings->aggregationmethod == RATING_AGGREGATE_NONE) {
return null;//ratings are turned off
}
- $useajax = !empty($CFG->enableajax);
-
- //include required Javascript
- if( !$havesetupjavascript && $useajax ) {
- $this->page->requires->js_init_call('M.core_rating.init');
- $havesetupjavascript = true;
- }
-
- //check the item we're rating was created in the assessable time window
- $inassessablewindow = true;
- if ( $rating->settings->assesstimestart && $rating->settings->assesstimefinish ) {
- if ($rating->itemtimecreated < $rating->settings->assesstimestart || $rating->itemtimecreated > $rating->settings->assesstimefinish) {
- $inassessablewindow = false;
- }
- }
+ $ratingmanager = new rating_manager();
+ // Initialise the JavaScript so ratings can be done by AJAX.
+ $ratingmanager->initialise_rating_javascript($this->page);
$strrate = get_string("rate", "rating");
$ratinghtml = ''; //the string we'll return
- //permissions check - can they view the aggregate?
- $canviewaggregate = false;
+ // permissions check - can they view the aggregate?
+ if ($rating->user_can_view_aggregate()) {
- //if its the current user's item and they have permission to view the aggregate on their own items
- if ( $rating->itemuserid==$USER->id && $rating->settings->permissions->view && $rating->settings->pluginpermissions->view) {
- $canviewaggregate = true;
- }
+ $aggregatelabel = $ratingmanager->get_aggregate_label($rating->settings->aggregationmethod);
+ $aggregatestr = $rating->get_aggregate_string();
- //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
- //Note that viewany doesnt mean you can see the aggregate or ratings of your own items
- if ( (empty($rating->itemuserid) or $rating->itemuserid!=$USER->id) && $rating->settings->permissions->viewany && $rating->settings->pluginpermissions->viewany ) {
- $canviewaggregate = true;
- }
-
- if ($canviewaggregate==true) {
- $aggregatelabel = '';
- switch ($rating->settings->aggregationmethod) {
- case RATING_AGGREGATE_AVERAGE :
- $aggregatelabel .= get_string("aggregateavg", "rating");
- break;
- case RATING_AGGREGATE_COUNT :
- $aggregatelabel .= get_string("aggregatecount", "rating");
- break;
- case RATING_AGGREGATE_MAXIMUM :
- $aggregatelabel .= get_string("aggregatemax", "rating");
- break;
- case RATING_AGGREGATE_MINIMUM :
- $aggregatelabel .= get_string("aggregatemin", "rating");
- break;
- case RATING_AGGREGATE_SUM :
- $aggregatelabel .= get_string("aggregatesum", "rating");
- break;
- }
- $aggregatelabel .= get_string('labelsep', 'langconfig');
-
- //$scalemax = 0;//no longer displaying scale max
- $aggregatestr = '';
-
- //only display aggregate if aggregation method isn't COUNT
- if ($rating->aggregate && $rating->settings->aggregationmethod!= RATING_AGGREGATE_COUNT) {
- if ($rating->settings->aggregationmethod!= RATING_AGGREGATE_SUM && is_array($rating->settings->scale->scaleitems)) {
- $aggregatestr .= $rating->settings->scale->scaleitems[round($rating->aggregate)];//round aggregate as we're using it as an index
- }
- else { //aggregation is SUM or the scale is numeric
- $aggregatestr .= round($rating->aggregate,1);
- }
+ $aggregatehtml = html_writer::tag('span', $aggregatestr, array('id' => 'ratingaggregate'.$rating->itemid)).' ';
+ $aggregatehtml .= html_writer::start_tag('span', array('id'=>"ratingcount{$rating->itemid}"));
+ if ($rating->count > 0) {
+ $aggregatehtml .= "({$rating->count})";
} else {
- $aggregatestr = '';
+ $aggregatehtml .= '-';
}
-
- $countstr = html_writer::start_tag('span', array('id'=>"ratingcount{$rating->itemid}"));
- if ($rating->count>0) {
- $countstr .= "({$rating->count})";
- }
- $countstr .= html_writer::end_tag('span');
-
- //$aggregatehtml = "{$ratingstr} / $scalemax ({$rating->count}) ";
- $aggregatehtml = "<span id='ratingaggregate{$rating->itemid}'>{$aggregatestr}</span> $countstr ";
+ $aggregatehtml .= html_writer::end_tag('span').' ';
$ratinghtml .= html_writer::tag('span', $aggregatelabel, array('class'=>'rating-aggregate-label'));
if ($rating->settings->permissions->viewall && $rating->settings->pluginpermissions->viewall) {
- $url = "/rating/index.php?contextid={$rating->context->id}&itemid={$rating->itemid}&scaleid={$rating->settings->scale->id}";
- $nonpopuplink = new moodle_url($url);
- $popuplink = new moodle_url("$url&popup=1");
+
+ $nonpopuplink = $rating->get_view_ratings_url();
+ $popuplink = $rating->get_view_ratings_url(true);
$action = new popup_action('click', $popuplink, 'ratings', array('height' => 400, 'width' => 600));
$ratinghtml .= $this->action_link($nonpopuplink, $aggregatehtml, $action);
@@ -1508,81 +1451,45 @@ function render_rating(rating $rating) {
}
$formstart = null;
- //if the item doesn't belong to the current user, the user has permission to rate
- //and we're within the assessable period
- if ($rating->itemuserid!=$USER->id
- && $rating->settings->permissions->rate
- && $rating->settings->pluginpermissions->rate
- && $inassessablewindow) {
-
- //start the rating form
- $formstart = html_writer::start_tag('form',
- array('id'=>"postrating{$rating->itemid}", 'class'=>'postratingform', 'method'=>'post', 'action'=>"{$CFG->wwwroot}/rating/rate.php"));
-
- $formstart .= html_writer::start_tag('div', array('class'=>'ratingform'));
-
- //add the hidden inputs
-
- $attributes = array('type'=>'hidden', 'class'=>'ratinginput', 'name'=>'contextid', 'value'=>$rating->context->id);
- $formstart .= html_writer::empty_tag('input', $attributes);
-
- $attributes['name'] = 'component';
- $attributes['value'] = $rating->settings->component;
- $formstart .= html_writer::empty_tag('input', $attributes);
-
- $attributes['name'] = 'itemid';
- $attributes['value'] = $rating->itemid;
- $formstart .= html_writer::empty_tag('input', $attributes);
-
- $attributes['name'] = 'scaleid';
- $attributes['value'] = $rating->settings->scale->id;
- $formstart .= html_writer::empty_tag('input', $attributes);
-
- $attributes['name'] = 'returnurl';
- $attributes['value'] = $rating->settings->returnurl;
- $formstart .= html_writer::empty_tag('input', $attributes);
+ // if the item doesn't belong to the current user, the user has permission to rate
+ // and we're within the assessable period
+ if ($rating->user_can_rate()) {
- $attributes['name'] = 'rateduserid';
- $attributes['value'] = $rating->itemuserid;
- $formstart .= html_writer::empty_tag('input', $attributes);
+ $rateurl = $rating->get_rate_url();
+ $inputs = $rateurl->params();
- $attributes['name'] = 'aggregation';
- $attributes['value'] = $rating->settings->aggregationmethod;
- $formstart .= html_writer::empty_tag('input', $attributes);
-
- $attributes['name'] = 'sesskey';
- $attributes['value'] = sesskey();;
- $formstart .= html_writer::empty_tag('input', $attributes);
+ //start the rating form
+ $formattrs = array(
+ 'id' => "postrating{$rating->itemid}",
+ 'class' => 'postratingform',
+ 'method' => 'post',
+ 'action' => $rateurl->out_omit_querystring()
+ );
+ $formstart = html_writer::start_tag('form', $formattrs);
+ $formstart .= html_writer::start_tag('div', array('class' => 'ratingform'));
+
+ // add the hidden inputs
+ foreach ($inputs as $name => $value) {
+ $attributes = array('type' => 'hidden', 'class' => 'ratinginput', 'name' => $name, 'value' => $value);
+ $formstart .= html_writer::empty_tag('input', $attributes);
+ }
if (empty($ratinghtml)) {
$ratinghtml .= $strrate.': ';
}
-
$ratinghtml = $formstart.$ratinghtml;
- //generate an array of values for numeric scales
- $scalearray = $rating->settings->scale->scaleitems;
- if (!is_array($scalearray)) { //almost certainly a numerical scale
- $intscalearray = intval($scalearray);//just in case they've passed "5" instead of 5
- $scalearray = array();
- if( is_int($intscalearray) && $intscalearray>0 ) {
- for($i=0; $i<=$rating->settings->scale->scaleitems; $i++) {
- $scalearray[$i] = $i;
- }
- }
- }
-
- $scalearray = array(RATING_UNSET_RATING => $strrate.'...') + $scalearray;
- $ratinghtml .= html_writer::select($scalearray, 'rating', $rating->rating, false, array('class'=>'postratingmenu ratinginput','id'=>'menurating'.$rating->itemid));
+ $scalearray = array(RATING_UNSET_RATING => $strrate.'...') + $rating->settings->scale->scaleitems;
+ $scaleattrs = array('class'=>'postratingmenu ratinginput','id'=>'menurating'.$rating->itemid);
+ $ratinghtml .= html_writer::select($scalearray, 'rating', $rating->rating, false, $scaleattrs);
//output submit button
-
$ratinghtml .= html_writer::start_tag('span', array('class'=>"ratingsubmit"));
- $attributes = array('type'=>'submit', 'class'=>'postratingmenusubmit', 'id'=>'postratingsubmit'.$rating->itemid, 'value'=>s(get_string('rate', 'rating')));
+ $attributes = array('type' => 'submit', 'class' => 'postratingmenusubmit', 'id' => 'postratingsubmit'.$rating->itemid, 'value' => s(get_string('rate', 'rating')));
$ratinghtml .= html_writer::empty_tag('input', $attributes);
- if (is_array($rating->settings->scale->scaleitems)) {
+ if (!$rating->settings->scale->isnumeric) {
$ratinghtml .= $this->help_icon_scale($rating->settings->scale->courseid, $rating->settings->scale);
}
$ratinghtml .= html_writer::end_tag('span');
@@ -2424,7 +2331,7 @@ protected function render_navigation_node(navigation_node $item) {
$content = $icon.$content; // use CSS for spacing of icons
}
if ($item->helpbutton !== null) {
- $content = trim($item->helpbutton).html_writer::tag('span', $content, array('class'=>'clearhelpbutton'));
+ $content = trim($item->helpbutton).html_writer::tag('span', $content, array('class'=>'clearhelpbutton', 'tabindex'=>'0'));
}
if ($content === '') {
return '';
@@ -2447,7 +2354,7 @@ protected function render_navigation_node(navigation_node $item) {
$content = html_writer::link($item->action, $content, $attributes);
} else if (is_string($item->action) || empty($item->action)) {
- $attributes = array();
+ $attributes = array('tabindex'=>'0'); //add tab support to span but still maintain character stream sequence.
if ($title !== '') {
$attributes['title'] = $title;
}
View
2  lib/rsslib.php
@@ -280,7 +280,7 @@ function rss_add_items($items) {
$result .= rss_add_enclosures($item);
$result .= rss_full_tag('pubDate',3,false,gmdate('D, d M Y H:i:s',$item->pubdate).' GMT'); # MDL-12563
//Include the author if exists
- if (isset($item->author)) {
+ if (isset($item->author) && !empty($item->author)) {
//$result .= rss_full_tag('author',3,false,$item->author);
//We put it in the description instead because it's more important
//for moodle than most other feeds, and most rss software seems to ignore
View
2  message/lib.php
@@ -1966,6 +1966,8 @@ function message_post_message($userfrom, $userto, $message, $format) {
* Returns a list of all user ids who have used messaging in the site
* This was the simple way to code the SQL ... is it going to blow up
* on large datasets?
+ *
+ * @todo: deprecated - to be deleted in 2.2
*/
function message_get_participants() {
global $CFG, $DB;
View
2  mod/assignment/lib.php
@@ -2772,6 +2772,8 @@ function assignment_grade_item_delete($assignment) {
/**
* Returns the users with data in one assignment (students and teachers)
*
+ * @todo: deprecated - to be deleted in 2.2
+ *
* @param $assignmentid int
* @return array of user objects
*/
View
3  mod/chat/lib.php
@@ -430,7 +430,8 @@ function chat_cron () {
* Returns the users with data in one chat
* (users with records in chat_messages, students)
*
- * @global object
+ * @todo: deprecated - to be deleted in 2.2
+ *
* @param int $chatid
* @param int $groupid
* @return array
View
2  mod/choice/lib.php
@@ -591,6 +591,8 @@ function choice_delete_instance($id) {
* Returns the users with data in one choice
* (users with records in choice_responses, students)
*
+ * @todo: deprecated - to be deleted in 2.2
+ *
* @param int $choiceid
* @return array
*/
View
2  mod/choice/renderer.php
@@ -40,7 +40,7 @@ public function display_options($options, $coursemoduleid, $vertical = false) {
$layoutclass = 'vertical';
}
$target = new moodle_url('/mod/choice/view.php');
- $attributes = array('method'=>'POST', 'target'=>$target, 'class'=> $layoutclass);
+ $attributes = array('method'=>'POST', 'action'=>$target, 'class'=> $layoutclass);
$html = html_writer::start_tag('form', $attributes);
$html .= html_writer::start_tag('ul', array('class'=>'choices' ));
View
8 mod/data/backup/moodle2/backup_data_stepslib.php
@@ -69,7 +69,7 @@ protected function define_structure() {
$ratings = new backup_nested_element('ratings');
$rating = new backup_nested_element('rating', array('id'), array(
- 'scaleid', 'value', 'userid', 'timecreated', 'timemodified'));
+ 'component', 'ratingarea', 'scaleid', 'value', 'userid', 'timecreated', 'timemodified'));
// Build the tree
$data->add_child($fields);
@@ -99,8 +99,10 @@ protected function define_structure() {
$content->set_source_table('data_content', array('recordid' => backup::VAR_PARENTID));
- $rating->set_source_table('rating', array('contextid' => backup::VAR_CONTEXTID,
- 'itemid' => backup::VAR_PARENTID));
+ $rating->set_source_table('rating', array('contextid' => backup::VAR_CONTEXTID,
+ 'itemid' => backup::VAR_PARENTID,
+ 'component' => 'mod_data',
+ 'ratingarea' => 'entry'));
$rating->set_source_alias('rating', 'value');
}
View
8 mod/data/backup/moodle2/restore_data_stepslib.php
@@ -138,6 +138,14 @@ protected function process_data_rating($data) {
$data->timecreated = $this->apply_date_offset($data->timecreated);
$data->timemodified = $this->apply_date_offset($data->timemodified);
+ // We need to check that component and ratingarea are both set here.
+ if (empty($data->component)) {
+ $data->component = 'mod_data';
+ }
+ if (empty($data->ratingarea)) {
+ $data->ratingarea = 'entry';
+ }
+
$newitemid = $DB->insert_record('rating', $data);
}
View
23 mod/data/db/upgrade.php
@@ -315,6 +315,29 @@ function xmldb_data_upgrade($oldversion) {
upgrade_mod_savepoint(true, 2010100101, 'data');
}
+ if ($oldversion < 2011052300) {
+ // rating.component and rating.ratingarea have now been added as mandatory fields.
+ // Presently you can only rate data entries so component = 'mod_data' and ratingarea = 'entry'
+ // for all ratings with a data context.
+ // We want to update all ratings that belong to a data context and don't already have a
+ // component set.
+ // This could take a while reset upgrade timeout to 5 min
+ upgrade_set_timeout(60 * 20);
+ $sql = "UPDATE {rating}
+ SET component = 'mod_data', ratingarea = 'entry'
+ WHERE contextid IN (
+ SELECT ctx.id
+ FROM {context} ctx
+ JOIN {course_modules} cm ON cm.id = ctx.instanceid
+ JOIN {modules} m ON m.id = cm.module
+ WHERE ctx.contextlevel = 70 AND
+ m.name = 'data'
+ ) AND component = 'unknown'";
+ $DB->execute($sql);
+
+ upgrade_mod_savepoint(true, 2011052300, 'data');
+ }
+
return true;
}
View
147 mod/data/lib.php
@@ -527,10 +527,10 @@ function data_generate_default_template(&$data, $template, $recordid=0, $form=fa
$cell->attributes['class'] = 'controls';
$table->data[] = new html_table_row(array($cell));
} else if ($template == 'asearchtemplate') {
- $row = new html_table_row(get_string('authorfirstname', 'data').': ', '##firstname##');
+ $row = new html_table_row(array(get_string('authorfirstname', 'data').': ', '##firstname##'));
$row->attributes['class'] = 'searchcontrols';
$table->data[] = $row;
- $row = new html_table_row(get_string('authorlastname', 'data').': ', '##lastname##');
+ $row = new html_table_row(array(get_string('authorlastname', 'data').': ', '##lastname##'));
$row->attributes['class'] = 'searchcontrols';
$table->data[] = $row;
}
@@ -1016,9 +1016,10 @@ function data_get_user_grades($data, $userid=0) {
global $CFG;
require_once($CFG->dirroot.'/rating/lib.php');
- $rm = new rating_manager();
- $ratingoptions = new stdclass();
+ $ratingoptions = new stdClass;
+ $ratingoptions->component = 'mod_data';
+ $ratingoptions->ratingarea = 'entry';
$ratingoptions->modulename = 'data';
$ratingoptions->moduleid = $data->id;
@@ -1028,6 +1029,7 @@ function data_get_user_grades($data, $userid=0) {
$ratingoptions->itemtable = 'data_records';
$ratingoptions->itemtableusercolumn = 'userid';
+ $rm = new rating_manager();
return $rm->get_user_grades($ratingoptions);
}
@@ -1144,25 +1146,46 @@ function data_grade_item_delete($data) {
/**
* returns a list of participants of this database
*
- * @global object
+ * Returns the users with data in one data
+ * (users with records in data_records, data_comments and ratings)
+ *
+ * @todo: deprecated - to be deleted in 2.2
+ *
+ * @param int $dataid
* @return array
*/
function data_get_participants($dataid) {
-// Returns the users with data in one data
-// (users with records in data_records, data_comments and ratings)
global $DB;
- $records = $DB->get_records_sql("SELECT DISTINCT u.id, u.id
- FROM {user} u, {data_records} r
- WHERE r.dataid = ? AND u.id = r.userid", array($dataid));
-
- $comments = $DB->get_records_sql("SELECT DISTINCT u.id, u.id
- FROM {user} u, {data_records} r, {comments} c
- WHERE r.dataid = ? AND u.id = r.userid AND r.id = c.itemid AND c.commentarea='database_entry'", array($dataid));
-
- $ratings = $DB->get_records_sql("SELECT DISTINCT u.id, u.id
- FROM {user} u, {data_records} r, {ratings} a
- WHERE r.dataid = ? AND u.id = r.userid AND r.id = a.itemid", array($dataid));
+ $params = array('dataid' => $dataid);
+
+ $sql = "SELECT DISTINCT u.id, u.id
+ FROM {user} u,
+ {data_records} r
+ WHERE r.dataid = :dataid AND
+ u.id = r.userid";
+ $records = $DB->get_records_sql($sql, $params);
+
+ $sql = "SELECT DISTINCT u.id, u.id
+ FROM {user} u,
+ {data_records} r,
+ {comments} c
+ WHERE r.dataid = ? AND
+ u.id = r.userid AND
+ r.id = c.itemid AND
+ c.commentarea = 'database_entry'";
+ $comments = $DB->get_records_sql($sql, $params);
+
+ $sql = "SELECT DISTINCT u.id, u.id
+ FROM {user} u,
+ {data_records} r,
+ {ratings} a
+ WHERE r.dataid = ? AND
+ u.id = r.userid AND
+ r.id = a.itemid AND
+ a.component = 'mod_data' AND
+ a.ratingarea = 'entry'";
+ $ratings = $DB->get_records_sql($sql, $params);
$participants = array();
@@ -1357,20 +1380,23 @@ function data_print_template($template, $records, $data, $search='', $page=0, $r
/**
* Return rating related permissions
- * @param string $options the context id
+ *
+ * @param string $contextid the context id
+ * @param string $component the component to get rating permissions for
+ * @param string $ratingarea the rating area to get permissions for
* @return array an associative array of the user's rating permissions
*/
-function data_rating_permissions($options) {
- $contextid = $options;
- $context = get_context_instance_by_id($contextid);
-
- if (!$context) {
- print_error('invalidcontext');
+function data_rating_permissions($contextid, $component, $ratingarea) {
+ $context = get_context_instance_by_id($contextid, MUST_EXIST);
+ if ($component != 'mod_data' || $ratingarea != 'entry') {
return null;
- } else {
- $ret = new stdclass();
- return array('view'=>has_capability('mod/data:viewrating',$context), 'viewany'=>has_capability('mod/data:viewanyrating',$context), 'viewall'=>has_capability('mod/data:viewallratings',$context), 'rate'=>has_capability('mod/data:rate',$context));
}
+ return array(
+ 'view' => has_capability('mod/data:viewrating',$context),
+ 'viewany' => has_capability('mod/data:viewanyrating',$context),
+ 'viewall' => has_capability('mod/data:viewallratings',$context),
+ 'rate' => has_capability('mod/data:rate',$context)
+ );
}
/**
@@ -1387,14 +1413,22 @@ function data_rating_permissions($options) {
function data_rating_validate($params) {
global $DB, $USER;
- if (!array_key_exists('itemid', $params)
- || !array_key_exists('context', $params)
- || !array_key_exists('rateduserid', $params)
- || !array_key_exists('scaleid', $params)) {
- throw new rating_exception('missingparameter');
+ // Check the component is mod_data
+ if ($params['component'] != 'mod_data') {
+ throw new rating_exception('invalidcomponent');
}
- $datasql = "SELECT d.id as did, d.scale, d.course, r.userid as userid, d.approval, r.approved, r.timecreated, d.assesstimestart, d.assesstimefinish, r.groupid
+ // Check the ratingarea is entry (the only rating area in data module)
+ if ($params['ratingarea'] != 'entry') {
+ throw new rating_exception('invalidratingarea');
+ }
+
+ // Check the rateduserid is not the current user .. you can't rate your own entries
+ if ($params['rateduserid'] == $USER->id) {
+ throw new rating_exception('nopermissiontorate');
+ }
+
+ $datasql = "SELECT d.id as dataid, d.scale, d.course, r.userid as userid, d.approval, r.approved, r.timecreated, d.assesstimestart, d.assesstimefinish, r.groupid
FROM {data_records} r
JOIN {data} d ON r.dataid = d.id
WHERE r.id = :itemid";
@@ -1409,22 +1443,17 @@ function data_rating_validate($params) {
throw new rating_exception('invalidscaleid');
}
- if ($info->userid == $USER->id) {
- //user is attempting to rate their own glossary entry
- throw new rating_exception('nopermissiontorate');
- }
+ //check that the submitted rating is valid for the scale
- if ($info->userid != $params['rateduserid']) {
- //supplied user ID doesnt match the user ID from the database
- throw new rating_exception('invaliduserid');
+ // lower limit
+ if ($params['rating'] < 0 && $params['rating'] != RATING_UNSET_RATING) {
+ throw new rating_exception('invalidnum');
}
- //check that the submitted rating is valid for the scale
- if ($params['rating'] < 0) {
- throw new rating_exception('invalidnum');
- } else if ($info->scale < 0) {
+ // upper limit
+ if ($info->scale < 0) {
//its a custom scale
- $scalerecord = $DB->get_record('scale', array('id' => -$params['scaleid']));
+ $scalerecord = $DB->get_record('scale', array('id' => -$info->scale));
if ($scalerecord) {
$scalearray = explode(',', $scalerecord->scale);
if ($params['rating'] > count($scalearray)) {
@@ -1443,30 +1472,24 @@ function data_rating_validate($params) {
throw new rating_exception('nopermissiontorate');
}
- //check the item we're rating was created in the assessable time window
+ // check the item we're rating was created in the assessable time window
if (!empty($info->assesstimestart) && !empty($info->assesstimefinish)) {
if ($info->timecreated < $info->assesstimestart || $info->timecreated > $info->assesstimefinish) {
throw new rating_exception('notavailable');
}
}
- $dataid = $info->did;
- $groupid = $info->groupid;
- $courseid = $info->course;
+ $course = $DB->get_record('course', array('id'=>$info->course), '*', MUST_EXIST);
+ $cm = get_coursemodule_from_instance('data', $info->dataid, $course->id, false, MUST_EXIST);
+ $context = get_context_instance(CONTEXT_MODULE, $cm->id, MUST_EXIST);
- $cm = get_coursemodule_from_instance('data', $dataid);
- if (empty($cm)) {
- throw new rating_exception('unknowncontext');
- }
- $context = get_context_instance(CONTEXT_MODULE, $cm->id);
-
- //if the supplied context doesnt match the item's context
- if (empty($context) || $context->id != $params['context']->id) {
+ // if the supplied context doesnt match the item's context
+ if ($context->id != $params['context']->id) {
throw new rating_exception('invalidcontext');
}
// Make sure groups allow this user to see the item they're rating
- $course = $DB->get_record('course', array('id'=>$courseid), '*', MUST_EXIST);
+ $groupid = $info->groupid;
if ($groupid > 0 and $groupmode = groups_get_activity_groupmode($cm, $course)) { // Groups are being used
if (!groups_group_exists($groupid)) { // Can't find group
throw new rating_exception('cannotfindgroup');//something is wrong
@@ -1676,7 +1699,7 @@ function data_print_preference_form($data, $perpage, $search, $sort='', $order='
*/
function data_print_ratings($data, $record) {
global $OUTPUT;
- if( !empty($record->rating) ){
+ if (!empty($record->rating)){
echo $OUTPUT->render($record->rating);
}
}
@@ -2523,7 +2546,9 @@ function data_reset_userdata($data) {
WHERE d.course=?";
$rm = new rating_manager();
- $ratingdeloptions = new stdclass();
+ $ratingdeloptions = new stdClass;
+ $ratingdeloptions->component = 'mod_data';
+ $ratingdeloptions->ratingarea = 'entry';
// delete entries if requested
if (!empty($data->reset_data)) {
View
4 mod/data/version.php
@@ -5,8 +5,8 @@
// This fragment is called by /admin/index.php
////////////////////////////////////////////////////////////////////////////////
-$module->version = 2010100101;
-$module->requires = 2010080300; // Requires this Moodle version
+$module->version = 2011052300;
+$module->requires = 2011052300; // Requires this Moodle version
$module->cron = 60;
View
5 mod/data/view.php
@@ -668,10 +668,11 @@
//data_print_template() only adds ratings for singletemplate which is why we're attaching them here
//attach ratings to data records
require_once($CFG->dirroot.'/rating/lib.php');
- if ($data->assessed!=RATING_AGGREGATE_NONE) {
- $ratingoptions = new stdclass();
+ if ($data->assessed != RATING_AGGREGATE_NONE) {
+ $ratingoptions = new stdClass;
$ratingoptions->context = $context;
$ratingoptions->component = 'mod_data';
+ $ratingoptions->ratingarea = 'entry';
$ratingoptions->items = $records;
$ratingoptions->aggregate = $data->assessed;//the aggregation method
$ratingoptions->scaleid = $data->scale;
View
1  mod/feedback/lib.php
@@ -467,6 +467,7 @@ function feedback_cron () {
}
/**
+ * @todo: deprecated - to be deleted in 2.2
* @return bool false
*/
function feedback_get_participants($feedbackid) {
View
2  mod/folder/lib.php
@@ -205,6 +205,8 @@ function folder_user_complete($course, $user, $mod, $folder) {
/**
* Returns the users with data in one folder
*
+ * @todo: deprecated - to be deleted in 2.2
+ *
* @param int $folderid
* @return bool false
*/
View
8 mod/forum/backup/moodle2/backup_forum_stepslib.php
@@ -63,7 +63,7 @@ protected function define_structure() {
$ratings = new backup_nested_element('ratings');
$rating = new backup_nested_element('rating', array('id'), array(
- 'scaleid', 'value', 'userid', 'timecreated', 'timemodified'));
+ 'component', 'ratingarea', 'scaleid', 'value', 'userid', 'timecreated', 'timemodified'));
$subscriptions = new backup_nested_element('subscriptions');
@@ -125,8 +125,10 @@ protected function define_structure() {
$track->set_source_table('forum_track_prefs', array('forumid' => backup::VAR_PARENTID));
- $rating->set_source_table('rating', array('contextid' => backup::VAR_CONTEXTID,
- 'itemid' => backup::VAR_PARENTID));
+ $rating->set_source_table('rating', array('contextid' => backup::VAR_CONTEXTID,
+ 'component' => 'mod_forum',
+ 'ratingarea' => 'post',
+ 'itemid' => backup::VAR_PARENTID));
$rating->set_source_alias('rating', 'value');
}
View
8 mod/forum/backup/moodle2/restore_forum_stepslib.php
@@ -126,6 +126,14 @@ protected function process_forum_rating($data) {
$data->timecreated = $this->apply_date_offset($data->timecreated);
$data->timemodified = $this->apply_date_offset($data->timemodified);
+ // We need to check that component and ratingarea are both set here.
+ if (empty($data->component)) {
+ $data->component = 'mod_forum';
+ }
+ if (empty($data->ratingarea)) {
+ $data->ratingarea = 'post';
+ }
+
$newitemid = $DB->insert_record('rating', $data);
}
View
22 mod/forum/db/upgrade.php
@@ -315,6 +315,28 @@ function xmldb_forum_upgrade($oldversion) {
upgrade_mod_savepoint(true, 2010091900, 'forum');
}
+ if ($oldversion < 2011052300) {
+ // rating.component and rating.ratingarea have now been added as mandatory fields.
+ // Presently you can only rate forum posts so component = 'mod_forum' and ratingarea = 'post'
+ // for all ratings with a forum context.
+ // We want to update all ratings that belong to a forum context and don't already have a
+ // component set.
+ // This could take a while reset upgrade timeout to 5 min
+ upgrade_set_timeout(60 * 20);
+ $sql = "UPDATE {rating}
+ SET component = 'mod_forum', ratingarea = 'post'
+ WHERE contextid IN (
+ SELECT ctx.id
+ FROM {context} ctx
+ JOIN {course_modules} cm ON cm.id = ctx.instanceid
+ JOIN {modules} m ON m.id = cm.module
+ WHERE ctx.contextlevel = 70 AND
+ m.name = 'forum'
+ ) AND component = 'unknown'";
+ $DB->execute($sql);
+
+ upgrade_mod_savepoint(true, 2011052300, 'forum');
+ }
return true;
}
View
300 mod/forum/lib.php
@@ -1445,25 +1445,25 @@ function forum_print_recent_activity($course, $viewfullnames, $timestart) {
* @param int $userid optional user id, 0 means all users
* @return array array of grades, false if none
*/
-function forum_get_user_grades($forum, $userid=0) {
+function forum_get_user_grades($forum, $userid = 0) {
global $CFG;
require_once($CFG->dirroot.'/rating/lib.php');
- $rm = new rating_manager();
- $ratingoptions = new stdclass();
+ $ratingoptions = new stdClass;
+ $ratingoptions->component = 'mod_forum';
+ $ratingoptions->ratingarea = 'post';
//need these to work backwards to get a context id. Is there a better way to get contextid from a module instance?
$ratingoptions->modulename = 'forum';
$ratingoptions->moduleid = $forum->id;
- //$ratingoptions->cmidnumber = $forum->cmidnumber;
-
$ratingoptions->userid = $userid;
$ratingoptions->aggregationmethod = $forum->assessed;
$ratingoptions->scaleid = $forum->scale;
$ratingoptions->itemtable = 'forum_posts';
$ratingoptions->itemtableusercolumn = 'userid';
+ $rm = new rating_manager();
return $rm->get_user_grades($ratingoptions);
}
@@ -1586,8 +1586,8 @@ function forum_grade_item_delete($forum) {
* Returns the users with data in one forum
* (users with records in forum_subscriptions, forum_posts, students)
*
- * @global object
- * @global object
+ * @todo: deprecated - to be deleted in 2.2
+ *
* @param int $forumid
* @return mixed array or false if none
*/
@@ -1595,31 +1595,35 @@ function forum_get_participants($forumid) {
global $CFG, $DB;
+ $params = array('forumid' => $forumid);
+
//Get students from forum_subscriptions
- $st_subscriptions = $DB->get_records_sql("SELECT DISTINCT u.id, u.id
- FROM {user} u,
- {forum_subscriptions} s
- WHERE s.forum = ? AND
- u.id = s.userid", array($forumid));
+ $sql = "SELECT DISTINCT u.id, u.id
+ FROM {user} u,
+ {forum_subscriptions} s
+ WHERE s.forum = :forumid AND
+ u.id = s.userid";
+ $st_subscriptions = $DB->get_records_sql($sql, $params);
+
//Get students from forum_posts
- $st_posts = $DB->get_records_sql("SELECT DISTINCT u.id, u.id
- FROM {user} u,
- {forum_discussions} d,
- {forum_posts} p
- WHERE d.forum = ? AND
- p.discussion = d.id AND
- u.id = p.userid", array($forumid));
+ $sql = "SELECT DISTINCT u.id, u.id
+ FROM {user} u,
+ {forum_discussions} d,
+ {forum_posts} p
+ WHERE d.forum = :forumid AND
+ p.discussion = d.id AND
+ u.id = p.userid";
+ $st_posts = $DB->get_records_sql($sql, $params);
//Get students from the ratings table
- $st_ratings = $DB->get_records_sql("SELECT DISTINCT u.id, u.id
- FROM {user} u,
- {forum_discussions} d,
- {forum_posts} p,
- {ratings} r
- WHERE d.forum = ? AND
- p.discussion = d.id AND
- r.post = p.id AND
- u.id = r.userid", array($forumid));
+ $sql = "SELECT DISTINCT r.userid, r.userid AS id
+ FROM {forum_discussions} d
+ JOIN {forum_posts} p ON p.discussion = d.id
+ JOIN {rating} r on r.itemid = p.id
+ WHERE d.forum = :forumid AND
+ r.component = 'mod_forum' AND
+ r.ratingarea = 'post'";
+ $st_ratings = $DB->get_records_sql($sql, $params);
//Add st_posts to st_subscriptions
if ($st_posts) {
@@ -2050,29 +2054,29 @@ function forum_search_posts($searchterms, $courseid=0, $limitfrom=0, $limitnum=5
/**
* Returns a list of ratings for a particular post - sorted.
*
- * @global object
- * @global object
+ * TODO: Check if this function is actually used anywhere.
+ * Up until the fix for MDL-27471 this function wasn't even returning.
+ *
+ * @param stdClass $context
* @param int $postid
* @param string $sort
* @return array Array of ratings or false
*/
-function forum_get_ratings($context, $postid, $sort="u.firstname ASC") {
- global $PAGE;
-
- $options = new stdclass();
- $options->context = $PAGE->context;
+function forum_get_ratings($context, $postid, $sort = "u.firstname ASC") {
+ $options = new stdClass;
+ $options->context = $context;
+ $options->component = 'mod_forum';
+ $options->ratingarea = 'post';
$options->itemid = $postid;
$options->sort = "ORDER BY $sort";
$rm = new rating_manager();
- $rm->get_all_ratings_for_item($options);
+ return $rm->get_all_ratings_for_item($options);
}
/**
* Returns a list of all new posts that have not been mailed yet
*
- * @global object
- * @global object
* @param int $starttime posts created after this time
* @param int $endtime posts created before this
* @param int $now used for timed discussions only
@@ -2456,29 +2460,35 @@ function forum_count_discussions($forum, $cm, $course) {
/**
* How many posts by other users are unrated by a given user in the given discussion?
*
- * @global object
- * @global object
+ * TODO: Is this function still used anywhere?
+ *
* @param int $discussionid
* @param int $userid
* @return mixed
*/
function forum_count_unrated_posts($discussionid, $userid) {
global $CFG, $DB;
- if ($posts = $DB->get_record_sql("SELECT count(*) as num
- FROM {forum_posts}
- WHERE parent > 0
- AND discussion = ?
- AND userid <> ? ", array($discussionid, $userid))) {
-
- if ($rated = $DB->get_record_sql("SELECT count(*) as num
- FROM {forum_posts} p,
- {rating} r
- WHERE p.discussion = ?
- AND p.id = r.itemid
- AND r.userid = ?", array($discussionid, $userid))) {
- $difference = $posts->num - $rated->num;
- if ($difference > 0) {
- return $difference;
+
+ $sql = "SELECT COUNT(*) as num
+ FROM {forum_posts}
+ WHERE parent > 0
+ AND discussion = :discussionid
+ AND userid <> :userid";
+ $params = array('discussionid' => $discussionid, 'userid' => $userid);
+ $posts = $DB->get_record_sql($sql, $params);
+ if ($posts) {
+ $sql = "SELECT count(*) as num
+ FROM {forum_posts} p,
+ {rating} r
+ WHERE p.discussion = :discussionid AND
+ p.id = r.itemid AND
+ r.userid = userid AND
+ r.component = 'mod_forum' AND
+ r.ratingarea = 'post'";
+ $rated = $DB->get_record_sql($sql, $params);
+ if ($rated) {
+ if ($posts->num > $rated->num) {
+ return $posts->num - $rated->num;
} else {
return 0; // Just in case there was a counting error
}
@@ -3451,24 +3461,31 @@ function forum_print_post($post, $discussion, $forum, &$cm, $course, $ownpost=fa
/**
* Return rating related permissions
+ *
* @param string $options the context id
* @return array an associative array of the user's rating permissions
*/
-function forum_rating_permissions($contextid) {
- $context = get_context_instance_by_id($contextid);
-
- if (!$context) {
- print_error('invalidcontext');
+function forum_rating_permissions($contextid, $component, $ratingarea) {
+ $context = get_context_instance_by_id($contextid, MUST_EXIST);
+ if ($component != 'mod_forum' || $ratingarea != 'post') {
+ // We don't know about this component/ratingarea so just return null to get the
+ // default restrictive permissions.
return null;
- } else {
- return array('view'=>has_capability('mod/forum:viewrating',$context), 'viewany'=>has_capability('mod/forum:viewanyrating',$context), 'viewall'=>has_capability('mod/forum:viewallratings',$context), 'rate'=>has_capability('mod/forum:rate',$context));
}
+ return array(
+ 'view' => has_capability('mod/forum:viewrating', $context),
+ 'viewany' => has_capability('mod/forum:viewanyrating', $context),
+ 'viewall' => has_capability('mod/forum:viewallratings', $context),
+ 'rate' => has_capability('mod/forum:rate', $context)
+ );
}
/**
* Validates a submitted rating
* @param array $params submitted data
* context => object the context in which the rated items exists [required]
+ * component => The component for this module - should always be mod_forum [required]