Skip to content
Browse files

MDL-22414 Merge branch 'master' into backup-convert

Conflicts:
	theme/base/style/core.css
  • Loading branch information...
2 parents fad0f24 + 6911fa1 commit b61cf3ff8a3467a9245325f5157c1b20e090c116 @mudrd8mz mudrd8mz committed Jun 9, 2011
Showing with 10,400 additions and 5,774 deletions.
  1. +1 −1 admin/generator.php
  2. +0 −1 admin/index.php
  3. +70 −0 admin/message.php
  4. +248 −226 admin/qtypes.php
  5. +2 −2 admin/registration/confirmregistration.php
  6. +2 −2 admin/registration/index.php
  7. +5 −3 admin/report/questioninstances/index.php
  8. +1 −1 admin/report/spamcleaner/module.js
  9. +1 −1 admin/roles/module.js
  10. +2 −0 admin/settings/appearance.php
  11. +3 −3 admin/settings/courses.php
  12. +24 −0 admin/settings/plugins.php
  13. +2 −51 admin/settings/server.php
  14. +2 −2 admin/webservice/service_user_settings.php
  15. +2 −2 admin/webservice/testclient.php
  16. +1 −1 admin/webservice/tokens.php
  17. +1 −2 backup/moodle2/backup_qtype_plugin.class.php
  18. +64 −53 backup/moodle2/backup_stepslib.php
  19. +24 −4 backup/moodle2/restore_qtype_plugin.class.php
  20. +157 −47 backup/moodle2/restore_stepslib.php
  21. +50 −23 enrol/externallib.php
  22. +14 −27 lang/en/admin.php
  23. +1 −0 lang/en/error.php
  24. +17 −1 lang/en/message.php
  25. +3 −3 lang/en/moodle.php
  26. +2 −0 lang/en/notes.php
  27. +137 −0 lang/en/question.php
  28. +3 −0 lang/en/webservice.php
  29. +458 −6 lib/adminlib.php
  30. +2 −2 lib/cronlib.php
  31. +93 −14 lib/db/install.xml
  32. +4 −0 lib/db/messages.php
  33. +44 −0 lib/db/services.php
  34. +432 −0 lib/db/upgrade.php
  35. +51 −0 lib/db/upgradelib.php
  36. +2 −0 lib/html2text.php
  37. +0 −1 lib/installlib.php
  38. +191 −85 lib/messagelib.php
  39. +176 −27 lib/moodlelib.php
  40. +38 −2 lib/outputrenderers.php
  41. +1 −0 lib/outputrequirementslib.php
  42. +39 −34 lib/pagelib.php
  43. +506 −1,939 lib/questionlib.php
  44. +6 −1 lib/resourcelib.php
  45. +296 −1 lib/simpletest/testmoodlelib.php
  46. +38 −118 lib/simpletest/testquestionlib.php
  47. +4 −3 lib/simpletest/testweblib.php
  48. +37 −0 lib/upgradelib.php
  49. +21 −2 lib/weblib.php
  50. +48 −0 local/qeupgradehelper/README.txt
  51. +162 −0 local/qeupgradehelper/afterupgradelib.php
  52. +76 −0 local/qeupgradehelper/convertquiz.php
  53. +69 −0 local/qeupgradehelper/cronsetup.php
  54. +62 −0 local/qeupgradehelper/cronsetup_form.php
  55. +74 −0 local/qeupgradehelper/extracttestcase.php
  56. +62 −0 local/qeupgradehelper/extracttestcase_form.php
  57. +55 −0 local/qeupgradehelper/index.php
  58. +83 −0 local/qeupgradehelper/lang/en/local_qeupgradehelper.php
  59. +74 −0 local/qeupgradehelper/lib.php
  60. +61 −0 local/qeupgradehelper/listpreupgrade.php
  61. +49 −0 local/qeupgradehelper/listtodo.php
  62. +50 −0 local/qeupgradehelper/listupgraded.php
  63. +661 −0 local/qeupgradehelper/locallib.php
  64. +115 −0 local/qeupgradehelper/partialupgrade-example.php
  65. +168 −0 local/qeupgradehelper/renderer.php
  66. +72 −0 local/qeupgradehelper/resetquiz.php
  67. +32 −0 local/qeupgradehelper/settings.php
  68. +6 −0 local/qeupgradehelper/styles.css
  69. +29 −0 local/qeupgradehelper/version.php
  70. +115 −0 message/defaultoutputs.php
  71. +30 −121 message/edit.php
  72. +159 −0 message/externallib.php
  73. +283 −88 message/lib.php
  74. +46 −1 message/module.js
  75. +16 −1 message/output/email/lang/en/message_email.php
  76. +1 −1 message/output/email/message_output_email.php
  77. +44 −0 message/output/email/settings.php
  78. +11 −1 message/output/jabber/lang/en/message_jabber.php
  79. +51 −41 message/output/jabber/message_output_jabber.php
  80. +34 −0 message/output/jabber/settings.php
  81. +16 −0 message/output/lib.php
  82. +346 −0 message/renderer.php
  83. +2 −4 mod/assignment/lib.php
  84. +2 −0 mod/forum/lang/en/forum.php
  85. +6 −3 mod/forum/settings.php
  86. +1 −1 mod/glossary/backup/moodle2/backup_glossary_stepslib.php
  87. +142 −85 mod/quiz/accessrules.php
  88. +27 −5 mod/quiz/addrandom.php
  89. +45 −9 mod/quiz/addrandomform.php
  90. +116 −156 mod/quiz/attempt.php
  91. +619 −603 mod/quiz/attemptlib.php
  92. +13 −7 mod/quiz/backup/moodle2/backup_quiz_activity_task.class.php
  93. +25 −15 mod/quiz/backup/moodle2/backup_quiz_stepslib.php
  94. +84 −49 mod/quiz/backup/moodle2/restore_quiz_activity_task.class.php
  95. +127 −20 mod/quiz/backup/moodle2/restore_quiz_stepslib.php
  96. +62 −52 mod/quiz/comment.php
  97. +22 −1 mod/quiz/db/access.php
  98. +32 −8 mod/quiz/db/install.php
  99. +63 −54 mod/quiz/db/install.xml
  100. +1 −2 mod/quiz/db/log.php
  101. +10 −13 mod/quiz/db/messages.php
  102. +26 −1 mod/quiz/db/subplugins.php
  103. +725 −103 mod/quiz/db/upgrade.php
  104. +76 −0 mod/quiz/db/upgradelib.php
  105. +32 −15 mod/quiz/edit.js
  106. +117 −88 mod/quiz/edit.php
  107. +354 −280 mod/quiz/editlib.php
  108. +51 −26 mod/quiz/grade.php
  109. +167 −147 mod/quiz/index.php
  110. +34 −126 mod/quiz/lang/en/quiz.php
  111. +341 −471 mod/quiz/lib.php
  112. +640 −485 mod/quiz/locallib.php
Sorry, we could not display the entire diff because too many files (577) changed.
View
2 admin/generator.php
@@ -622,7 +622,7 @@ public function generate_questions($courses, $modules) {
require_once($CFG->libdir .'/questionlib.php');
require_once($CFG->dirroot .'/mod/quiz/editlib.php');
$questions = array();
- $questionsmenu = question_type_menu();
+ $questionsmenu = question_bank::get_creatable_qtypes();
$questiontypes = array();
foreach ($questionsmenu as $qtype => $qname) {
$questiontypes[] = $qtype;
View
1 admin/index.php
@@ -355,7 +355,6 @@
}
// login user and let him set password and admin details
$adminuser->newadminuser = 1;
- message_set_default_message_preferences($adminuser);
complete_user_login($adminuser, false);
redirect("$CFG->wwwroot/user/editadvanced.php?id=$adminuser->id"); // Edit thyself
View
70 admin/message.php
@@ -0,0 +1,70 @@
+<?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/>.
+
+/**
+ * Message outputs configuration page
+ *
+ * @package message
+ * @copyright 2011 Lancaster University Network Services Limited
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+require_once(dirname(__FILE__) . '/../config.php');
+require_once($CFG->dirroot . '/message/lib.php');
+require_once($CFG->libdir.'/adminlib.php');
+
+// This is an admin page
+admin_externalpage_setup('managemessageoutputs');
+
+// Require site configuration capability
+require_capability('moodle/site:config', get_context_instance(CONTEXT_SYSTEM));
+
+// Get the submitted params
+$disable = optional_param('disable', 0, PARAM_INT);
+$enable = optional_param('enable', 0, PARAM_INT);
+
+if (!empty($disable) && confirm_sesskey()) {
+ if (!$processor = $DB->get_record('message_processors', array('id'=>$disable))) {
+ print_error('outputdoesnotexist', 'message');
+ }
+ $DB->set_field('message_processors', 'enabled', '0', array('id'=>$processor->id)); // Disable output
+}
+
+if (!empty($enable) && confirm_sesskey() ) {
+ if (!$processor = $DB->get_record('message_processors', array('id'=>$enable))) {
+ print_error('outputdoesnotexist', 'message');
+ }
+ $DB->set_field('message_processors', 'enabled', '1', array('id'=>$processor->id)); // Enable output
+}
+
+if ($disable || $enable) {
+ $url = new moodle_url('message.php');
+ redirect($url);
+}
+// Page settings
+$PAGE->set_context(get_context_instance(CONTEXT_SYSTEM));
+
+// Grab the renderer
+$renderer = $PAGE->get_renderer('core', 'message');
+
+// Display the manage message outputs interface
+$processors = get_message_processors();
+$messageoutputs = $renderer->manage_messageoutputs($processors);
+
+// Display the page
+echo $OUTPUT->header();
+echo $OUTPUT->heading(get_string('managemessageoutputs', 'message'));
+echo $messageoutputs;
+echo $OUTPUT->footer();
View
474 admin/qtypes.php
@@ -1,275 +1,297 @@
<?php
-// Allows the admin to manage question types.
-
- require_once(dirname(__FILE__) . '/../config.php');
- require_once($CFG->libdir . '/questionlib.php');
- require_once($CFG->libdir . '/adminlib.php');
- require_once($CFG->libdir . '/tablelib.php');
-
-/// Check permissions.
- require_login();
- $systemcontext = get_context_instance(CONTEXT_SYSTEM);
- require_capability('moodle/question:config', $systemcontext);
- $canviewreports = has_capability('report/questioninstances:view', $systemcontext);
-
- admin_externalpage_setup('manageqtypes');
-
-/// Get some data we will need - question counts and which types are needed.
- $counts = $DB->get_records_sql("
- SELECT qtype, COUNT(1) as numquestions, SUM(hidden) as numhidden
- FROM {question} GROUP BY qtype", array());
- $needed = array();
- foreach ($QTYPES as $qtypename => $qtype) {
- if (!isset($counts[$qtypename])) {
- $counts[$qtypename] = new stdClass;
- $counts[$qtypename]->numquestions = 0;
- $counts[$qtypename]->numhidden = 0;
- }
- $needed[$qtypename] = $counts[$qtypename]->numquestions > 0;
- $counts[$qtypename]->numquestions -= $counts[$qtypename]->numhidden;
- }
- $needed['missingtype'] = true; // The system needs the missing question type.
- foreach ($QTYPES as $qtypename => $qtype) {
- foreach ($qtype->requires_qtypes() as $reqtype) {
- $needed[$reqtype] = true;
- }
+
+// 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/>.
+
+/**
+ * Allows the admin to manage question types.
+ *
+ * @package moodlecore
+ * @subpackage questionbank
+ * @copyright 2008 Tim Hunt
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+
+require_once(dirname(__FILE__) . '/../config.php');
+require_once($CFG->libdir . '/questionlib.php');
+require_once($CFG->libdir . '/adminlib.php');
+require_once($CFG->libdir . '/tablelib.php');
+
+// Check permissions.
+require_login();
+$systemcontext = get_context_instance(CONTEXT_SYSTEM);
+require_capability('moodle/question:config', $systemcontext);
+$canviewreports = has_capability('report/questioninstances:view', $systemcontext);
+
+admin_externalpage_setup('manageqtypes');
+
+$qtypes = question_bank::get_all_qtypes();
+
+// Get some data we will need - question counts and which types are needed.
+$counts = $DB->get_records_sql("
+ SELECT qtype, COUNT(1) as numquestions, SUM(hidden) as numhidden
+ FROM {question} GROUP BY qtype", array());
+$needed = array();
+foreach ($qtypes as $qtypename => $qtype) {
+ if (!isset($counts[$qtypename])) {
+ $counts[$qtypename] = new stdClass;
+ $counts[$qtypename]->numquestions = 0;
+ $counts[$qtypename]->numhidden = 0;
}
- foreach ($counts as $qtypename => $count) {
- if (!isset($QTYPES[$qtypename])) {
- $counts['missingtype']->numquestions += $count->numquestions - $count->numhidden;
- $counts['missingtype']->numhidden += $count->numhidden;
- }
+ $needed[$qtypename] = $counts[$qtypename]->numquestions > 0;
+ $counts[$qtypename]->numquestions -= $counts[$qtypename]->numhidden;
+}
+$needed['missingtype'] = true; // The system needs the missing question type.
+foreach ($qtypes as $qtypename => $qtype) {
+ foreach ($qtype->requires_qtypes() as $reqtype) {
+ $needed[$reqtype] = true;
}
-
-/// Work of the correct sort order.
- $config = get_config('question');
- $sortedqtypes = array();
- foreach ($QTYPES as $qtypename => $qtype) {
- $sortedqtypes[$qtypename] = $qtype->local_name();
+}
+foreach ($counts as $qtypename => $count) {
+ if (!isset($qtypes[$qtypename])) {
+ $counts['missingtype']->numquestions += $count->numquestions - $count->numhidden;
+ $counts['missingtype']->numhidden += $count->numhidden;
}
- $sortedqtypes = question_sort_qtype_array($sortedqtypes, $config);
+}
-/// Process actions ============================================================
+// Work of the correct sort order.
+$config = get_config('question');
+$sortedqtypes = array();
+foreach ($qtypes as $qtypename => $qtype) {
+ $sortedqtypes[$qtypename] = $qtype->local_name();
+}
+$sortedqtypes = question_bank::sort_qtype_array($sortedqtypes, $config);
- // Disable.
- if (($disable = optional_param('disable', '', PARAM_SAFEDIR)) && confirm_sesskey()) {
- if (!isset($QTYPES[$disable])) {
- print_error('unknownquestiontype', 'question', admin_url('qtypes.php'), $disable);
- }
+// Process actions ============================================================
- set_config($disable . '_disabled', 1, 'question');
- redirect(admin_url('qtypes.php'));
+// Disable.
+if (($disable = optional_param('disable', '', PARAM_SAFEDIR)) && confirm_sesskey()) {
+ if (!isset($qtypes[$disable])) {
+ print_error('unknownquestiontype', 'question', new moodle_url('/admin/qtypes.php'), $disable);
}
- // Enable.
- if (($enable = optional_param('enable', '', PARAM_SAFEDIR)) && confirm_sesskey()) {
- if (!isset($QTYPES[$enable])) {
- print_error('unknownquestiontype', 'question', admin_url('qtypes.php'), $enable);
- }
+ set_config($disable . '_disabled', 1, 'question');
+ redirect(admin_url('qtypes.php'));
+}
- if (!$QTYPES[$enable]->menu_name()) {
- print_error('cannotenable', 'question', admin_url('qtypes.php'), $enable);
- }
+// Enable.
+if (($enable = optional_param('enable', '', PARAM_SAFEDIR)) && confirm_sesskey()) {
+ if (!isset($qtypes[$enable])) {
+ print_error('unknownquestiontype', 'question', new moodle_url('/admin/qtypes.php'), $enable);
+ }
- unset_config($enable . '_disabled', 'question');
- redirect(admin_url('qtypes.php'));
+ if (!$qtypes[$enable]->menu_name()) {
+ print_error('cannotenable', 'question', new moodle_url('/admin/qtypes.php'), $enable);
}
- // Move up in order.
- if (($up = optional_param('up', '', PARAM_SAFEDIR)) && confirm_sesskey()) {
- if (!isset($QTYPES[$up])) {
- print_error('unknownquestiontype', 'question', admin_url('qtypes.php'), $up);
- }
+ unset_config($enable . '_disabled', 'question');
+ redirect(new moodle_url('/admin/qtypes.php'));
+}
- $neworder = question_reorder_qtypes($sortedqtypes, $up, -1);
- question_save_qtype_order($neworder, $config);
- redirect(admin_url('qtypes.php'));
+// Move up in order.
+if (($up = optional_param('up', '', PARAM_SAFEDIR)) && confirm_sesskey()) {
+ if (!isset($qtypes[$up])) {
+ print_error('unknownquestiontype', 'question', new moodle_url('/admin/qtypes.php'), $up);
}
- // Move down in order.
- if (($down = optional_param('down', '', PARAM_SAFEDIR)) && confirm_sesskey()) {
- if (!isset($QTYPES[$down])) {
- print_error('unknownquestiontype', 'question', admin_url('qtypes.php'), $down);
- }
+ $neworder = question_reorder_qtypes($sortedqtypes, $up, -1);
+ question_save_qtype_order($neworder, $config);
+ redirect(new moodle_url('/admin/qtypes.php'));
+}
- $neworder = question_reorder_qtypes($sortedqtypes, $down, +1);
- question_save_qtype_order($neworder, $config);
- redirect(admin_url('qtypes.php'));
+// Move down in order.
+if (($down = optional_param('down', '', PARAM_SAFEDIR)) && confirm_sesskey()) {
+ if (!isset($qtypes[$down])) {
+ print_error('unknownquestiontype', 'question', admin_url('qtypes.php'), $down);
}
- // Delete.
- if (($delete = optional_param('delete', '', PARAM_SAFEDIR)) && confirm_sesskey()) {
- // Check it is OK to delete this question type.
- if ($delete == 'missingtype') {
- print_error('cannotdeletemissingqtype', 'admin', admin_url('qtypes.php'));
- }
+ $neworder = question_reorder_qtypes($sortedqtypes, $down, +1);
+ question_save_qtype_order($neworder, $config);
+ redirect(new moodle_url('/admin/qtypes.php'));
+}
- if (!isset($QTYPES[$delete])) {
- print_error('unknownquestiontype', 'question', admin_url('qtypes.php'), $delete);
- }
+// Delete.
+if (($delete = optional_param('delete', '', PARAM_SAFEDIR)) && confirm_sesskey()) {
+ // Check it is OK to delete this question type.
+ if ($delete == 'missingtype') {
+ print_error('cannotdeletemissingqtype', 'admin', new moodle_url('/admin/qtypes.php'));
+ }
- $qtypename = $QTYPES[$delete]->local_name();
- if ($counts[$delete]->numquestions + $counts[$delete]->numhidden > 0) {
- print_error('cannotdeleteqtypeinuse', 'admin', admin_url('qtypes.php'), $qtypename);
- }
+ if (!isset($qtypes[$delete])) {
+ print_error('unknownquestiontype', 'question', new moodle_url('/admin/qtypes.php'), $delete);
+ }
- if ($needed[$delete] > 0) {
- print_error('cannotdeleteqtypeneeded', 'admin', admin_url('qtypes.php'), $qtypename);
- }
+ $qtypename = $qtypes[$delete]->local_name();
+ if ($counts[$delete]->numquestions + $counts[$delete]->numhidden > 0) {
+ print_error('cannotdeleteqtypeinuse', 'admin', new moodle_url('/admin/qtypes.php'), $qtypename);
+ }
- // If not yet confirmed, display a confirmation message.
- if (!optional_param('confirm', '', PARAM_BOOL)) {
- $qtypename = $QTYPES[$delete]->local_name();
- echo $OUTPUT->header();
- echo $OUTPUT->heading(get_string('deleteqtypeareyousure', 'admin', $qtypename));
- echo $OUTPUT->confirm(get_string('deleteqtypeareyousuremessage', 'admin', $qtypename),
- admin_url('qtypes.php?delete=' . $delete . '&confirm=1'),
- admin_url('qtypes.php'));
- echo $OUTPUT->footer();
- exit;
- }
+ if ($needed[$delete] > 0) {
+ print_error('cannotdeleteqtypeneeded', 'admin', new moodle_url('/admin/qtypes.php'), $qtypename);
+ }
- // Do the deletion.
+ // If not yet confirmed, display a confirmation message.
+ if (!optional_param('confirm', '', PARAM_BOOL)) {
+ $qtypename = $qtypes[$delete]->local_name();
echo $OUTPUT->header();
- echo $OUTPUT->heading(get_string('deletingqtype', 'admin', $qtypename));
-
- // Delete any configuration records.
- if (!unset_all_config_for_plugin('qtype_' . $delete)) {
- echo $OUTPUT->notification(get_string('errordeletingconfig', 'admin', 'qtype_' . $delete));
- }
- unset_config($delete . '_disabled', 'question');
- unset_config($delete . '_sortorder', 'question');
-
- // Then the tables themselves
- drop_plugin_tables($delete, $QTYPES[$delete]->plugin_dir() . '/db/install.xml', false);
-
- // Remove event handlers and dequeue pending events
- events_uninstall('qtype/' . $delete);
-
- $a->qtype = $qtypename;
- $a->directory = $QTYPES[$delete]->plugin_dir();
- echo $OUTPUT->box(get_string('qtypedeletefiles', 'admin', $a), 'generalbox', 'notice');
- echo $OUTPUT->continue_button(admin_url('qtypes.php'));
+ echo $OUTPUT->heading(get_string('deleteqtypeareyousure', 'admin', $qtypename));
+ echo $OUTPUT->confirm(get_string('deleteqtypeareyousuremessage', 'admin', $qtypename),
+ new moodle_url('/admin/qtypes.php', array('delete' => $delete, 'confirm' => 1)),
+ new moodle_url('/admin/qtypes.php'));
echo $OUTPUT->footer();
exit;
}
- // End of process actions ==================================================
-
-/// Print the page heading.
+ // Do the deletion.
echo $OUTPUT->header();
- echo $OUTPUT->heading(get_string('manageqtypes', 'admin'));
-
-/// Set up the table.
- $table = new flexible_table('qtypeadmintable');
- $table->define_columns(array('questiontype', 'numquestions', 'version', 'requires',
- 'availableto', 'delete', 'settings'));
- $table->define_headers(array(get_string('questiontype', 'admin'), get_string('numquestions', 'admin'),
- get_string('version'), get_string('requires', 'admin'), get_string('availableq', 'question'),
- get_string('delete'), get_string('settings')));
- $table->set_attribute('id', 'qtypes');
- $table->set_attribute('class', 'generaltable generalbox boxaligncenter boxwidthwide');
- $table->setup();
-
-/// Add a row for each question type.
- $createabletypes = question_type_menu();
- foreach ($sortedqtypes as $qtypename => $localname) {
- $qtype = $QTYPES[$qtypename];
- $row = array();
-
- // Question icon and name.
- $fakequestion = new stdClass;
- $fakequestion->qtype = $qtypename;
- $icon = print_question_icon($fakequestion, true);
- $row[] = $icon . ' ' . $localname;
-
- // Number of questions of this type.
- if ($counts[$qtypename]->numquestions + $counts[$qtypename]->numhidden > 0) {
- if ($counts[$qtypename]->numhidden > 0) {
- $strcount = get_string('numquestionsandhidden', 'admin', $counts[$qtypename]);
- } else {
- $strcount = $counts[$qtypename]->numquestions;
- }
- if ($canviewreports) {
- $row[] = '<a href="' . admin_url('/report/questioninstances/index.php?qtype=' . $qtypename) .
- '" title="' . get_string('showdetails', 'admin') . '">' . $strcount . '</a>';
- } else {
- $strcount;
- }
- } else {
- $row[] = 0;
- }
+ echo $OUTPUT->heading(get_string('deletingqtype', 'admin', $qtypename));
- // Question version number.
- $version = get_config('qtype_' . $qtypename, 'version');
- if ($version) {
- $row[] = $version;
- } else {
- $row[] = '<span class="disabled">' . get_string('nodatabase', 'admin') . '</span>';
- }
+ // Delete any configuration records.
+ if (!unset_all_config_for_plugin('qtype_' . $delete)) {
+ echo $OUTPUT->notification(get_string('errordeletingconfig', 'admin', 'qtype_' . $delete));
+ }
+ unset_config($delete . '_disabled', 'question');
+ unset_config($delete . '_sortorder', 'question');
- // Other question types required by this one.
- $requiredtypes = $qtype->requires_qtypes();
- $strtypes = array();
- if (!empty($requiredtypes)) {
- foreach ($requiredtypes as $required) {
- $strtypes[] = $QTYPES[$required]->local_name();
- }
- $row[] = implode(', ', $strtypes);
+ // Then the tables themselves
+ drop_plugin_tables($delete, $qtypes[$delete]->plugin_dir() . '/db/install.xml', false);
+
+ // Remove event handlers and dequeue pending events
+ events_uninstall('qtype/' . $delete);
+
+ $a->qtype = $qtypename;
+ $a->directory = $qtypes[$delete]->plugin_dir();
+ echo $OUTPUT->box(get_string('qtypedeletefiles', 'admin', $a), 'generalbox', 'notice');
+ echo $OUTPUT->continue_button(new moodle_url('/admin/qtypes.php'));
+ echo $OUTPUT->footer();
+ exit;
+}
+
+// End of process actions ==================================================
+
+// Print the page heading.
+echo $OUTPUT->header();
+echo $OUTPUT->heading(get_string('manageqtypes', 'admin'));
+
+// Set up the table.
+$table = new flexible_table('qtypeadmintable');
+$table->define_baseurl(new moodle_url('/admin/qtypes.php'));
+$table->define_columns(array('questiontype', 'numquestions', 'version', 'requires',
+ 'availableto', 'delete', 'settings'));
+$table->define_headers(array(get_string('questiontype', 'admin'), get_string('numquestions', 'admin'),
+ get_string('version'), get_string('requires', 'admin'), get_string('availableq', 'question'),
+ get_string('delete'), get_string('settings')));
+$table->set_attribute('id', 'qtypes');
+$table->set_attribute('class', 'generaltable generalbox boxaligncenter boxwidthwide');
+$table->setup();
+
+// Add a row for each question type.
+$createabletypes = question_bank::get_creatable_qtypes();
+foreach ($sortedqtypes as $qtypename => $localname) {
+ $qtype = $qtypes[$qtypename];
+ $row = array();
+
+ // Question icon and name.
+ $fakequestion = new stdClass;
+ $fakequestion->qtype = $qtypename;
+ $icon = print_question_icon($fakequestion, true);
+ $row[] = $icon . ' ' . $localname;
+
+ // Number of questions of this type.
+ if ($counts[$qtypename]->numquestions + $counts[$qtypename]->numhidden > 0) {
+ if ($counts[$qtypename]->numhidden > 0) {
+ $strcount = get_string('numquestionsandhidden', 'admin', $counts[$qtypename]);
} else {
- $row[] = '';
+ $strcount = $counts[$qtypename]->numquestions;
}
-
- // Are people allowed to create new questions of this type?
- $rowclass = '';
- if ($qtype->menu_name()) {
- $createable = isset($createabletypes[$qtypename]);
- $icons = enable_disable_button($qtypename, $createable);
- if (!$createable) {
- $rowclass = 'dimmed_text';
- }
+ if ($canviewreports) {
+ $row[] = '<a href="' . new moodle_url('/admin/report/questioninstances/index.php', array('qtype' => $qtypename)) .
+ '" title="' . get_string('showdetails', 'admin') . '">' . $strcount . '</a>';
} else {
- $icons = '<img src="' . $OUTPUT->pix_url('spacer') . '" alt="" class="spacer" />';
+ $strcount;
}
+ } else {
+ $row[] = 0;
+ }
- // Move icons.
- $icons .= icon_html('up', $qtypename, 't/up', get_string('up'), '');
- $icons .= icon_html('down', $qtypename, 't/down', get_string('down'), '');
- $row[] = $icons;
+ // Question version number.
+ $version = get_config('qtype_' . $qtypename, 'version');
+ if ($version) {
+ $row[] = $version;
+ } else {
+ $row[] = '<span class="disabled">' . get_string('nodatabase', 'admin') . '</span>';
+ }
- // Delete link, if available.
- if ($needed[$qtypename]) {
- $row[] = '';
- } else {
- $row[] = '<a href="' . admin_url('qtypes.php?delete=' . $qtypename .
- '&amp;sesskey=' . sesskey()) . '" title="' .
- get_string('uninstallqtype', 'admin') . '">' . get_string('delete') . '</a>';
+ // Other question types required by this one.
+ $requiredtypes = $qtype->requires_qtypes();
+ $strtypes = array();
+ if (!empty($requiredtypes)) {
+ foreach ($requiredtypes as $required) {
+ $strtypes[] = $qtypes[$required]->local_name();
}
+ $row[] = implode(', ', $strtypes);
+ } else {
+ $row[] = '';
+ }
- // Settings link, if available.
- $settings = admin_get_root()->locate('qtypesetting' . $qtypename);
- if ($settings instanceof admin_externalpage) {
- $row[] = '<a href="' . $settings->url .
- '">' . get_string('settings') . '</a>';
- } else if ($settings instanceof admin_settingpage) {
- $row[] = '<a href="' . admin_url('settings.php?section=qtypesetting' . $qtypename) .
- '">' . get_string('settings') . '</a>';
- } else {
- $row[] = '';
+ // Are people allowed to create new questions of this type?
+ $rowclass = '';
+ if ($qtype->menu_name()) {
+ $createable = isset($createabletypes[$qtypename]);
+ $icons = enable_disable_button($qtypename, $createable);
+ if (!$createable) {
+ $rowclass = 'dimmed_text';
}
-
- $table->add_data($row, $rowclass);
+ } else {
+ $icons = '<img src="' . $OUTPUT->pix_url('spacer') . '" alt="" class="spacer" />';
}
- $table->finish_output();
+ // Move icons.
+ $icons .= icon_html('up', $qtypename, 't/up', get_string('up'), '');
+ $icons .= icon_html('down', $qtypename, 't/down', get_string('down'), '');
+ $row[] = $icons;
- echo $OUTPUT->footer();
+ // Delete link, if available.
+ if ($needed[$qtypename]) {
+ $row[] = '';
+ } else {
+ $row[] = '<a href="' . new moodle_url('/admin/qtypes.php', array('delete' => $qtypename,
+ 'sesskey' => sesskey())) . '" title="' .
+ get_string('uninstallqtype', 'admin') . '">' . get_string('delete') . '</a>';
+ }
+
+ // Settings link, if available.
+ $settings = admin_get_root()->locate('qtypesetting' . $qtypename);
+ if ($settings instanceof admin_externalpage) {
+ $row[] = '<a href="' . $settings->url .
+ '">' . get_string('settings') . '</a>';
+ } else if ($settings instanceof admin_settingpage) {
+ $row[] = '<a href="' . new moodle_url('/admin/settings.php', array('section' => 'qtypesetting' . $qtypename)) .
+ '">' . get_string('settings') . '</a>';
+ } else {
+ $row[] = '';
+ }
-function admin_url($endbit) {
- global $CFG;
- return $CFG->wwwroot . '/' . $CFG->admin . '/' . $endbit;
+ $table->add_data($row, $rowclass);
}
+$table->finish_output();
+
+echo $OUTPUT->footer();
+
function enable_disable_button($qtypename, $createable) {
if ($createable) {
return icon_html('disable', $qtypename, 'i/hide', get_string('enabled', 'question'), get_string('disable'));
@@ -283,7 +305,7 @@ function icon_html($action, $qtypename, $icon, $alt, $tip) {
if ($tip) {
$tip = 'title="' . $tip . '" ';
}
- $html = ' <form action="' . admin_url('qtypes.php') . '" method="post"><div>';
+ $html = ' <form action="' . new moodle_url('/admin/qtypes.php') . '" method="post"><div>';
$html .= '<input type="hidden" name="sesskey" value="' . sesskey() . '" />';
$html .= '<input type="image" name="' . $action . '" value="' . $qtypename .
'" src="' . $OUTPUT->pix_url($icon) . '" alt="' . $alt . '" ' . $tip . '/>';
View
4 admin/registration/confirmregistration.php
@@ -47,7 +47,7 @@
admin_externalpage_setup('registrationindex');
if (!empty($error) and $error == 'urlalreadyexist') {
- throw new moodle_exception('urlalreadyregistered', 'hub',
+ throw new moodle_exception('urlalreadyregistered', 'hub',
$CFG->wwwroot . '/' . $CFG->admin . '/registration/index.php');
}
@@ -85,7 +85,7 @@
echo $OUTPUT->footer();
} else {
- throw new moodle_exception('wrongtoken', 'hub',
+ throw new moodle_exception('wrongtoken', 'hub',
$CFG->wwwroot . '/' . $CFG->admin . '/registration/index.php');
}
View
4 admin/registration/index.php
@@ -148,12 +148,12 @@
$moodleorghub = $registrationmanager->get_registeredhub(HUB_MOODLEORGHUBURL);
if (!empty($moodleorghub)) {
$registeredonmoodleorg = true;
- }
+ }
echo $OUTPUT->heading(get_string('registeron', 'hub'), 3, 'main');
echo $renderer->registrationselector($registeredonmoodleorg);
- if (extension_loaded('xmlrpc')) {
+ if (extension_loaded('xmlrpc')) {
$hubs = $registrationmanager->get_registered_on_hubs();
if (!empty($hubs)) {
echo $OUTPUT->heading(get_string('registeredon', 'hub'), 3, 'main');
View
8 admin/report/questioninstances/index.php
@@ -22,8 +22,9 @@
add_to_log(SITEID, "admin", "report questioninstances", "report/questioninstances/index.php?qtype=$requestedqtype", $requestedqtype);
// Prepare the list of capabilities to choose from
+$qtypes = question_bank::get_all_qtypes();
$qtypechoices = array();
-foreach ($QTYPES as $qtype) {
+foreach ($qtypes as $qtype) {
$qtypechoices[$qtype->name()] = $qtype->local_name();
}
@@ -45,7 +46,7 @@
// Work out the bits needed for the SQL WHERE clauses.
if ($requestedqtype == 'missingtype') {
- $othertypes = array_keys($QTYPES);
+ $othertypes = array_keys($qtypes);
$key = array_search('missingtype', $othertypes);
unset($othertypes[$key]);
list($sqlqtypetest, $params) = $DB->get_in_or_equal($othertypes, SQL_PARAMS_QM, '', false);
@@ -58,7 +59,8 @@
} else {
$sqlqtypetest = 'WHERE qtype = ?';
$params = array($requestedqtype);
- $title = get_string('reportforqtype', 'report_questioninstances', $QTYPES[$requestedqtype]->local_name());
+ $title = get_string('reportforqtype', 'report_questioninstances',
+ question_bank::get_qtype($requestedqtype)->local_name());
}
// Get the question counts, and all the context information, for each
View
2 admin/report/spamcleaner/module.js
@@ -108,7 +108,7 @@ M.report_spamcleaner = {
context.Y = Y;
context.me = me;
if (Y.one("#removeall_btn")) {
- Y.on("click", context.del_all, "#removeall_btn");
+ Y.on("click", context.del_all, "#removeall_btn");
}
});
}
View
2 admin/roles/module.js
@@ -138,7 +138,7 @@ M.core_role.init_cap_table_filter = function(Y, tableid, contextid) {
lastheading = null;
this.setFilterCookieValue(filtertext);
-
+
this.button.set('disabled', (filtertext == ''));
this.table.all('tr').each(function(row){
View
2 admin/settings/appearance.php
@@ -18,6 +18,8 @@
$temp->add(new admin_setting_configcheckbox('allowuserblockhiding', get_string('allowuserblockhiding', 'admin'), get_string('configallowuserblockhiding', 'admin'), 1));
$temp->add(new admin_setting_configcheckbox('allowblockstodock', get_string('allowblockstodock', 'admin'), get_string('configallowblockstodock', 'admin'), 1));
$temp->add(new admin_setting_configtextarea('custommenuitems', get_string('custommenuitems', 'admin'), get_string('configcustommenuitems', 'admin'), '', PARAM_TEXT, '50', '10'));
+ $temp->add(new admin_setting_configcheckbox('enabledevicedetection', get_string('enabledevicedetection', 'admin'), get_string('configenabledevicedetection', 'admin'), 1));
+ $temp->add(new admin_setting_devicedetectregex('devicedetectregex', get_string('devicedetectregex', 'admin'), get_string('devicedetectregex_desc', 'admin'), ''));
$ADMIN->add('themes', $temp);
$ADMIN->add('themes', new admin_externalpage('themeselector', get_string('themeselector','admin'), $CFG->wwwroot . '/theme/index.php'));
View
6 admin/settings/courses.php
@@ -139,7 +139,7 @@
400 => '400',
500 => '500');
$temp->add(new admin_setting_configselect('backup/backup_auto_keep', get_string('keep'), get_string('backupkeephelp'), 1, $keepoptoins));
-
+
$temp->add(new admin_setting_heading('automatedsettings', get_string('automatedsettings','backup'), ''));
$temp->add(new admin_setting_configcheckbox('backup/backup_auto_users', get_string('users'), get_string('backupusershelp'), 1));
@@ -152,8 +152,8 @@
$temp->add(new admin_setting_configcheckbox('backup/backup_auto_userscompletion', get_string('generaluserscompletion','backup'), get_string('configgeneraluserscompletion','backup'), 1));
$temp->add(new admin_setting_configcheckbox('backup/backup_auto_logs', get_string('logs'), get_string('backuplogshelp'), 0));
$temp->add(new admin_setting_configcheckbox('backup/backup_auto_histories', get_string('generalhistories','backup'), get_string('configgeneralhistories','backup'), 0));
-
-
+
+
//$temp->add(new admin_setting_configcheckbox('backup/backup_auto_messages', get_string('messages', 'message'), get_string('backupmessageshelp','message'), 0));
//$temp->add(new admin_setting_configcheckbox('backup/backup_auto_blogs', get_string('blogs', 'blog'), get_string('backupblogshelp','blog'), 0));
View
24 admin/settings/plugins.php
@@ -46,6 +46,27 @@
}
}
+ // message outputs
+ $ADMIN->add('modules', new admin_category('messageoutputs', get_string('messageoutputs', 'message')));
+ $ADMIN->add('messageoutputs', new admin_page_managemessageoutputs());
+ $ADMIN->add('messageoutputs', new admin_page_defaultmessageoutputs());
+ require_once($CFG->dirroot.'/message/lib.php');
+ $processors = get_message_processors();
+ foreach ($processors as $processor) {
+ $processorname = $processor->name;
+ if (!$processor->available) {
+ continue;
+ }
+ if ($processor->hassettings) {
+ $strprocessorname = get_string('pluginname', 'message_'.$processorname);
+ $settings = new admin_settingpage('messagesetting'.$processorname, $strprocessorname, 'moodle/site:config', !$processor->enabled);
+ include($CFG->dirroot.'/message/output/'.$processor->name.'/settings.php');
+ if ($settings) {
+ $ADMIN->add('messageoutputs', $settings);
+ }
+ }
+ }
+
// authentication plugins
$ADMIN->add('modules', new admin_category('authsettings', get_string('authentication', 'admin')));
@@ -324,6 +345,9 @@
$ADMIN->add('webservicesettings', $temp);
/// manage service
$temp = new admin_settingpage('externalservices', get_string('externalservices', 'webservice'));
+ $enablemobiledocurl = new moodle_url(get_docs_url('Enable_mobile_web_services'));
+ $enablemobiledoclink = html_writer::link($enablemobiledocurl, get_string('documentation'));
+ $temp->add(new admin_setting_enablemobileservice('enablemobilewebservice', get_string('enablemobilewebservice', 'admin'), get_string('configenablemobilewebservice', 'admin', $enablemobiledoclink), 0));
$temp->add(new admin_setting_heading('manageserviceshelpexplaination', get_string('information', 'webservice'), get_string('servicehelpexplanation', 'webservice')));
$temp->add(new admin_setting_manageexternalservices());
$ADMIN->add('webservicesettings', $temp);
View
53 admin/settings/server.php
@@ -17,46 +17,8 @@
-// "email" settingpage
-$temp = new admin_settingpage('mail', get_string('mail','admin'));
-$temp->add(new admin_setting_configtext('smtphosts', get_string('smtphosts', 'admin'), get_string('configsmtphosts', 'admin'), '', PARAM_RAW));
-$temp->add(new admin_setting_configtext('smtpuser', get_string('smtpuser', 'admin'), get_string('configsmtpuser', 'admin'), '', PARAM_NOTAGS));
-$temp->add(new admin_setting_configpasswordunmask('smtppass', get_string('smtppass', 'admin'), get_string('configsmtpuser', 'admin'), ''));
-$temp->add(new admin_setting_configtext('smtpmaxbulk', get_string('smtpmaxbulk', 'admin'), get_string('configsmtpmaxbulk', 'admin'), 1, PARAM_INT));
-$temp->add(new admin_setting_configtext('noreplyaddress', get_string('noreplyaddress', 'admin'), get_string('confignoreplyaddress', 'admin'), 'noreply@' . get_host_from_url($CFG->wwwroot), PARAM_NOTAGS));
-$temp->add(new admin_setting_configselect('digestmailtime', get_string('digestmailtime', 'admin'), get_string('configdigestmailtime', 'admin'), 17, array('00' => '00',
- '01' => '01',
- '02' => '02',
- '03' => '03',
- '04' => '04',
- '05' => '05',
- '06' => '06',
- '07' => '07',
- '08' => '08',
- '09' => '09',
- '10' => '10',
- '11' => '11',
- '12' => '12',
- '13' => '13',
- '14' => '14',
- '15' => '15',
- '16' => '16',
- '17' => '17',
- '18' => '18',
- '19' => '19',
- '20' => '20',
- '21' => '21',
- '22' => '22',
- '23' => '23')));
-$charsets = get_list_of_charsets();
-unset($charsets['UTF-8']); // not needed here
-$options = array();
-$options['0'] = 'UTF-8';
-$options = array_merge($options, $charsets);
-$temp->add(new admin_setting_configselect('sitemailcharset', get_string('sitemailcharset', 'admin'), get_string('configsitemailcharset','admin'), '0', $options));
-$temp->add(new admin_setting_configcheckbox('allowusermailcharset', get_string('allowusermailcharset', 'admin'), get_string('configallowusermailcharset', 'admin'), 0));
-$options = array('LF'=>'LF', 'CRLF'=>'CRLF');
-$temp->add(new admin_setting_configselect('mailnewline', get_string('mailnewline', 'admin'), get_string('configmailnewline','admin'), 'LF', $options));
+// "supportcontact" settingpage
+$temp = new admin_settingpage('supportcontact', get_string('supportcontact','admin'));
if (isloggedin()) {
global $USER;
$primaryadminemail = $USER->email;
@@ -73,17 +35,6 @@
$ADMIN->add('server', $temp);
-// Jabber settingpage
-$temp = new admin_settingpage('jabber', get_string('jabber', 'admin'));
-$temp->add(new admin_setting_configtext('jabberhost', get_string('jabberhost', 'admin'), get_string('configjabberhost', 'admin'), '', PARAM_RAW));
-$temp->add(new admin_setting_configtext('jabberserver', get_string('jabberserver', 'admin'), get_string('configjabberserver', 'admin'), '', PARAM_RAW));
-$temp->add(new admin_setting_configtext('jabberusername', get_string('jabberusername', 'admin'), get_string('configjabberusername', 'admin'), '', PARAM_RAW));
-$temp->add(new admin_setting_configpasswordunmask('jabberpassword', get_string('jabberpassword', 'admin'), get_string('configjabberpassword', 'admin'), ''));
-$temp->add(new admin_setting_configtext('jabberport', get_string('jabberport', 'admin'), get_string('configjabberport', 'admin'), 5222, PARAM_INT));
-$ADMIN->add('server', $temp);
-
-
-
// "sessionhandling" settingpage
$temp = new admin_settingpage('sessionhandling', get_string('sessionhandling', 'admin'));
$temp->add(new admin_setting_configcheckbox('dbsessions', get_string('dbsessions', 'admin'), get_string('configdbsessions', 'admin'), 1));
View
4 admin/webservice/service_user_settings.php
@@ -33,7 +33,7 @@
admin_externalpage_setup('externalserviceusersettings');
//define nav bar
-$PAGE->set_url('/' . $CFG->admin . '/webservice/service_user_settings.php',
+$PAGE->set_url('/' . $CFG->admin . '/webservice/service_user_settings.php',
array('id' => $serviceid, 'userid' => $userid));
$node = $PAGE->settingsnav->find('externalservices', navigation_node::TYPE_SETTING);
if ($node) {
@@ -62,7 +62,7 @@
$serviceuserinfo->id = $serviceuser->serviceuserid;
$serviceuserinfo->iprestriction = $settingsformdata->iprestriction;
$serviceuserinfo->validuntil = $settingsformdata->validuntil;
-
+
$webservicemanager->update_ws_authorised_user($serviceuserinfo);
//TODO: assign capability
View
4 admin/webservice/testclient.php
@@ -37,7 +37,7 @@
$PAGE->navbar->ignore_active(true);
$PAGE->navbar->add(get_string('administrationsite'));
$PAGE->navbar->add(get_string('development', 'admin'));
-$PAGE->navbar->add(get_string('testclient', 'webservice'),
+$PAGE->navbar->add(get_string('testclient', 'webservice'),
new moodle_url('/' . $CFG->admin . '/webservice/testclient.php'));
if (!empty($function)) {
$PAGE->navbar->add($function);
@@ -169,4 +169,4 @@
$mform->display();
echo $OUTPUT->footer();
die;
-}
+}
View
2 admin/webservice/tokens.php
@@ -94,7 +94,7 @@
die;
break;
- case 'delete':
+ case 'delete':
$token = $webservicemanager->get_created_by_user_ws_token($USER->id, $tokenid);
//Delete the token
View
3 backup/moodle2/backup_qtype_plugin.class.php
@@ -111,8 +111,7 @@ protected function add_question_numerical_options($element) {
// Define the elements
$options = new backup_nested_element('numerical_options');
$option = new backup_nested_element('numerical_option', array('id'), array(
- 'instructions', 'instructionsformat', 'showunits', 'unitsleft',
- 'unitgradingtype', 'unitpenalty'));
+ 'showunits', 'unitsleft', 'unitgradingtype', 'unitpenalty'));
// Build the tree
$element->add_child($options);
View
117 backup/moodle2/backup_stepslib.php
@@ -174,78 +174,76 @@ protected function prepare_activity_structure($activitystructure) {
/**
* Attach to $element (usually attempts) the needed backup structures
- * for question_states for a given question_attempt
+ * for question_usages and all the associated data.
*/
- protected function add_question_attempts_states($element, $questionattemptname) {
+ protected function add_question_usages($element, $usageidname) {
+ global $CFG;
+ require_once($CFG->dirroot . '/question/engine/lib.php');
+
// Check $element is one nested_backup_element
if (! $element instanceof backup_nested_element) {
throw new backup_step_exception('question_states_bad_parent_element', $element);
}
- // Check that the $questionattemptname is final element in $element
- if (! $element->get_final_element($questionattemptname)) {
- throw new backup_step_exception('question_states_bad_question_attempt_element', $questionattemptname);
+ if (! $element->get_final_element($usageidname)) {
+ throw new backup_step_exception('question_states_bad_question_attempt_element', $usageidname);
}
- // TODO: Some day we should stop these "encrypted" state->answers and
- // TODO: delegate to qtypes plugin to proper XML writting the needed info on each question
-
- // TODO: Should be doing here some introspection in the "answer" element, based on qtype,
- // TODO: to know which real questions are being used (for randoms and other qtypes...)
- // TODO: Not needed if consistency is guaranteed, but it isn't right now :-(
+ $quba = new backup_nested_element('question_usage', array('id'),
+ array('component', 'preferredbehaviour'));
- // Define the elements
- $states = new backup_nested_element('states');
- $state = new backup_nested_element('state', array('id'), array(
- 'question', 'seq_number', 'answer', 'timestamp',
- 'event', 'grade', 'raw_grade', 'penalty'));
+ $qas = new backup_nested_element('question_attempts');
+ $qa = new backup_nested_element('question_attempt', array('id'), array(
+ 'slot', 'behaviour', 'questionid', 'maxmark', 'minfraction',
+ 'flagged', 'questionsummary', 'rightanswer', 'responsesummary',
+ 'timemodified'));
- // Build the tree
- $element->add_child($states);
- $states->add_child($state);
-
- // Set the sources
- $state->set_source_table('question_states', array('attempt' => '../../' . $questionattemptname));
+ $steps = new backup_nested_element('steps');
+ $step = new backup_nested_element('step', array('id'), array(
+ 'sequencenumber', 'state', 'fraction', 'timecreated', 'userid'));
- // Annotate ids
- $state->annotate_ids('question', 'question');
- }
-
- /**
- * Attach to $element (usually attempts) the needed backup structures
- * for question_sessions for a given question_attempt
- */
- protected function add_question_attempts_sessions($element, $questionattemptname) {
- // Check $element is one nested_backup_element
- if (! $element instanceof backup_nested_element) {
- throw new backup_step_exception('question_sessions_bad_parent_element', $element);
- }
- // Check that the $questionattemptname is final element in $element
- if (! $element->get_final_element($questionattemptname)) {
- throw new backup_step_exception('question_sessions_bad_question_attempt_element', $questionattemptname);
- }
-
- // Define the elements
- $sessions = new backup_nested_element('sessions');
- $session = new backup_nested_element('session', array('id'), array(
- 'questionid', 'newest', 'newgraded', 'sumpenalty',
- 'manualcomment', 'manualcommentformat', 'flagged'));
+ $response = new backup_nested_element('response');
+ $variable = new backup_nested_element('variable', null, array('name', 'value'));
// Build the tree
- $element->add_child($sessions);
- $sessions->add_child($session);
+ $element->add_child($quba);
+ $quba->add_child($qas);
+ $qas->add_child($qa);
+ $qa->add_child($steps);
+ $steps->add_child($step);
+ $step->add_child($response);
+ $response->add_child($variable);
// Set the sources
- $session->set_source_table('question_sessions', array('attemptid' => '../../' . $questionattemptname));
+ $quba->set_source_table('question_usages',
+ array('id' => '../' . $usageidname));
+ $qa->set_source_sql('
+ SELECT *
+ FROM {question_attempts}
+ WHERE questionusageid = :questionusageid
+ ORDER BY slot',
+ array('questionusageid' => backup::VAR_PARENTID));
+ $step->set_source_sql('
+ SELECT *
+ FROM {question_attempt_steps}
+ WHERE questionattemptid = :questionattemptid
+ ORDER BY sequencenumber',
+ array('questionattemptid' => backup::VAR_PARENTID));
+ $variable->set_source_table('question_attempt_step_data',
+ array('attemptstepid' => backup::VAR_PARENTID));
// Annotate ids
- $session->annotate_ids('question', 'questionid');
+ $qa->annotate_ids('question', 'questionid');
+ $step->annotate_ids('user', 'userid');
// Annotate files
- // Note: question_sessions haven't files associated. On purpose manualcomment is lacking
- // support for them, so we don't need to annotated them here.
+ $fileareas = question_engine::get_all_response_file_areas();
+ foreach ($fileareas as $filearea) {
+ $step->annotate_files('question', $filearea, 'id');
+ }
}
}
+
/**
* backup structure step in charge of calculating the categories to be
* included in backup, based in the context being backuped (module/course)
@@ -1670,19 +1668,25 @@ protected function define_structure() {
$question = new backup_nested_element('question', array('id'), array(
'parent', 'name', 'questiontext', 'questiontextformat',
- 'generalfeedback', 'generalfeedbackformat', 'defaultgrade', 'penalty',
+ 'generalfeedback', 'generalfeedbackformat', 'defaultmark', 'penalty',
'qtype', 'length', 'stamp', 'version',
'hidden', 'timecreated', 'timemodified', 'createdby', 'modifiedby'));
// attach qtype plugin structure to $question element, only one allowed
$this->add_plugin_structure('qtype', $question, false);
+ $qhints = new backup_nested_element('question_hints');
+
+ $qhint = new backup_nested_element('question_hint', array('id'), array(
+ 'hint', 'hintformat', 'shownumcorrect', 'clearwrong', 'options'));
+
// Build the tree
$qcategories->add_child($qcategory);
$qcategory->add_child($questions);
-
$questions->add_child($question);
+ $question->add_child($qhints);
+ $qhints->add_child($qhint);
// Define the sources
@@ -1696,6 +1700,13 @@ protected function define_structure() {
$question->set_source_table('question', array('category' => backup::VAR_PARENTID));
+ $qhint->set_source_sql('
+ SELECT *
+ FROM {question_hints}
+ WHERE questionid = :questionid
+ ORDER BY id',
+ array('questionid' => backup::VAR_PARENTID));
+
// don't need to annotate ids nor files
// (already done by {@link backup_annotate_all_question_files}
View
28 backup/moodle2/restore_qtype_plugin.class.php
@@ -115,6 +115,23 @@ public function process_question_answer($data) {
$newquestionid = $this->get_new_parentid('question');
$questioncreated = $this->get_mappingid('question_created', $oldquestionid) ? true : false;
+ // In the past, there were some sloppily rounded fractions around. Fix them up.
+ $changes = array(
+ '-0.66666' => '-0.6666667',
+ '-0.33333' => '-0.3333333',
+ '-0.16666' => '-0.1666667',
+ '-0.142857' => '-0.1428571',
+ '0.11111' => '0.1111111',
+ '0.142857' => '0.1428571',
+ '0.16666' => '0.1666667',
+ '0.33333' => '0.3333333',
+ '0.333333' => '0.3333333',
+ '0.66666' => '0.6666667',
+ );
+ if (array_key_exists($data->fraction, $changes)) {
+ $data->fraction = $changes[$data->fraction];
+ }
+
// If the question has been created by restore, we need to create its question_answers too
if ($questioncreated) {
// Adjust some columns
@@ -298,11 +315,14 @@ public function process_question_dataset_item($data) {
}
/**
- * Decode one question_states for this qtype (default impl)
+ * Do any re-coding necessary in the student response.
+ * @param int $questionid the new id of the question
+ * @param int $sequencenumber of the step within the qusetion attempt.
+ * @param array the response data from the backup.
+ * @return array the recoded response.
*/
- public function recode_state_answer($state) {
- // By default, return answer unmodified, qtypes needing recode will override this
- return $state->answer;
+ public function recode_response($questionid, $sequencenumber, array $response) {
+ return $response;
}
/**
View
204 backup/moodle2/restore_stepslib.php
@@ -2310,6 +2310,17 @@ protected function process_question($data) {
// we have loaded qcatids there for all parsed questions
$data->category = $this->get_mappingid('question_category', $questionmapping->parentitemid);
+ // In the past, there were some very sloppy values of penalty. Fix them.
+ if ($data->penalty >= 0.33 && $data->penalty <= 0.34) {
+ $data->penalty = 0.3333333;
+ }
+ if ($data->penalty >= 0.66 && $data->penalty <= 0.67) {
+ $data->penalty = 0.6666667;
+ }
+ if ($data->penalty >= 1) {
+ $data->penalty = 1;
+ }
+
$data->timecreated = $this->apply_date_offset($data->timecreated);
$data->timemodified = $this->apply_date_offset($data->timemodified);
@@ -2339,6 +2350,47 @@ protected function process_question($data) {
// step will be in charge of restoring all the question files
}
+ protected function process_question_hint($data) {
+ global $DB;
+
+ $data = (object)$data;
+ $oldid = $data->id;
+
+ // Detect if the question is created or mapped
+ $oldquestionid = $this->get_old_parentid('question');
+ $newquestionid = $this->get_new_parentid('question');
+ $questioncreated = $this->get_mappingid('question_created', $oldquestionid) ? true : false;
+
+ // If the question has been created by restore, we need to create its question_answers too
+ if ($questioncreated) {
+ // Adjust some columns
+ $data->questionid = $newquestionid;
+ // Insert record
+ $newitemid = $DB->insert_record('question_answers', $data);
+
+ // The question existed, we need to map the existing question_answers
+ } else {
+ // Look in question_answers by answertext matching
+ $sql = 'SELECT id
+ FROM {question_hints}
+ WHERE questionid = ?
+ AND ' . $DB->sql_compare_text('hint', 255) . ' = ' . $DB->sql_compare_text('?', 255);
+ $params = array($newquestionid, $data->hint);
+ $newitemid = $DB->get_field_sql($sql, $params);
+ // If we haven't found the newitemid, something has gone really wrong, question in DB
+ // is missing answers, exception
+ if (!$newitemid) {
+ $info = new stdClass();
+ $info->filequestionid = $oldquestionid;
+ $info->dbquestionid = $newquestionid;
+ $info->hint = $data->hint;
+ throw new restore_step_exception('error_question_hint_missing_in_db', $info);
+ }
+ }
+ // Create mapping (we'll use this intensively when restoring question_states. And also answerfeedback files)
+ $this->set_mapping('question_hint', $oldid, $newitemid);
+ }
+
protected function after_execute() {
global $DB;
@@ -2461,6 +2513,8 @@ protected function define_execution() {
$oldctxid, $this->task->get_userid(), 'question_created', $question->itemid, $newctxid, true);
restore_dbops::send_files_to_pool($this->get_basepath(), $this->get_restoreid(), 'question', 'answerfeedback',
$oldctxid, $this->task->get_userid(), 'question_answer', null, $newctxid, true);
+ restore_dbops::send_files_to_pool($this->get_basepath(), $this->get_restoreid(), 'question', 'hint',
+ $oldctxid, $this->task->get_userid(), 'question_hint', null, $newctxid, true);
// Add qtype dependent files
$components = backup_qtype_plugin::get_components_and_fileareas($question->qtype);
foreach ($components as $component => $fileareas) {
@@ -2481,12 +2535,16 @@ protected function define_execution() {
* (like the quiz module), to support qtype plugins, states and sessions
*/
abstract class restore_questions_activity_structure_step extends restore_activity_structure_step {
+ /** @var array question_attempt->id to qtype. */
+ protected $qtypes = array();
+ /** @var array question_attempt->id to questionid. */
+ protected $newquestionids = array();
/**
* Attach below $element (usually attempts) the needed restore_path_elements
- * to restore question_states
+ * to restore question_usages and all they contain.
*/
- protected function add_question_attempts_states($element, &$paths) {
+ protected function add_question_usages($element, &$paths) {
// Check $element is restore_path_element
if (! $element instanceof restore_path_element) {
throw new restore_step_exception('element_must_be_restore_path_element', $element);
@@ -2495,70 +2553,114 @@ protected function add_question_attempts_states($element, &$paths) {
if (!is_array($paths)) {
throw new restore_step_exception('paths_must_be_array', $paths);
}
- $paths[] = new restore_path_element('question_state', $element->get_path() . '/states/state');
+ $paths[] = new restore_path_element('question_usage',
+ $element->get_path() . '/question_usage');
+ $paths[] = new restore_path_element('question_attempt',
+ $element->get_path() . '/question_usage/question_attempts/question_attempt');
+ $paths[] = new restore_path_element('question_attempt_step',
+ $element->get_path() . '/question_usage/question_attempts/question_attempt/steps/step',
+ true);
+ $paths[] = new restore_path_element('question_attempt_step_data',
+ $element->get_path() . '/question_usage/question_attempts/question_attempt/steps/step/response/variable');
+
+ // TODO Put back code for restoring legacy 2.0 backups.
+ // $paths[] = new restore_path_element('question_state', $element->get_path() . '/states/state');
+ // $paths[] = new restore_path_element('question_session', $element->get_path() . '/sessions/session');
}
/**
- * Attach below $element (usually attempts) the needed restore_path_elements
- * to restore question_sessions
+ * Process question_usages
*/
- protected function add_question_attempts_sessions($element, &$paths) {
- // Check $element is restore_path_element
- if (! $element instanceof restore_path_element) {
- throw new restore_step_exception('element_must_be_restore_path_element', $element);
- }
- // Check $paths is one array
- if (!is_array($paths)) {
- throw new restore_step_exception('paths_must_be_array', $paths);
- }
- $paths[] = new restore_path_element('question_session', $element->get_path() . '/sessions/session');
+ protected function process_question_usage($data) {
+ global $DB;
+
+ // Clear our caches.
+ $this->qtypes = array();
+ $this->newquestionids = array();
+
+ $data = (object)$data;
+ $oldid = $data->id;
+
+ $oldcontextid = $this->get_task()->get_old_contextid();
+ $data->contextid = $this->get_mappingid('context', $this->task->get_old_contextid());
+
+ // Everything ready, insert (no mapping needed)
+ $newitemid = $DB->insert_record('question_usages', $data);
+
+ $this->inform_new_usage_id($newitemid);
+
+ $this->set_mapping('question_usage', $oldid, $newitemid, false);
}
/**
- * Process question_states
+ * When process_question_usage creates the new usage, it calls this method
+ * to let the activity link to the new usage. For example, the quiz uses
+ * this method to set quiz_attempts.uniqueid to the new usage id.
+ * @param integer $newusageid
+ */
+ abstract protected function inform_new_usage_id($newusageid);
+
+ /**
+ * Process question_attempts
*/
- protected function process_question_state($data) {
+ protected function process_question_attempt($data) {
global $DB;
$data = (object)$data;
$oldid = $data->id;
+ $question = $this->get_mapping('question', $data->questionid);
- // Get complete question mapping, we'll need info
- $question = $this->get_mapping('question', $data->question);
+ $data->questionusageid = $this->get_new_parentid('question_usage');
+ $data->questionid = $question->newitemid;
+ $data->timemodified = $this->apply_date_offset($data->timemodified);
- // In the quiz_attempt mapping we are storing uniqueid
- // and not id, so this gets the correct question_attempt to point to
- $data->attempt = $this->get_new_parentid('quiz_attempt');
- $data->question = $question->newitemid;
- $data->answer = $this->restore_recode_answer($data, $question->info->qtype); // Delegate recoding of answer
- $data->timestamp= $this->apply_date_offset($data->timestamp);
+ $newitemid = $DB->insert_record('question_attempts', $data);
- // Everything ready, insert and create mapping (needed by question_sessions)
- $newitemid = $DB->insert_record('question_states', $data);
- $this->set_mapping('question_state', $oldid, $newitemid);
+ $this->set_mapping('question_attempt', $oldid, $newitemid);
+ $this->qtypes[$newitemid] = $question->info->qtype;
+ $this->newquestionids[$newitemid] = $data->questionid;
}
/**
- * Process question_sessions
+ * Process question_attempt_steps
*/
- protected function process_question_session($data) {
+ protected function process_question_attempt_step($data) {
global $DB;
$data = (object)$data;
$oldid = $data->id;
- // In the quiz_attempt mapping we are storing uniqueid
- // and not id, so this gets the correct question_attempt to point to
- $data->attemptid = $this->get_new_parentid('quiz_attempt');
- $data->questionid = $this->get_mappingid('question', $data->questionid);
- $data->newest = $this->get_mappingid('question_state', $data->newest);
- $data->newgraded = $this->get_mappingid('question_state', $data->newgraded);
+ // Pull out the response data.
+ $response = array();
+ if (!empty($data->response['variable'])) {
+ foreach ($data->response['variable'] as $variable) {
+ $response[$variable['name']] = $variable['value'];
+ }
+ }
+ unset($data->response);
- // Everything ready, insert (no mapping needed)
- $newitemid = $DB->insert_record('question_sessions', $data);
+ $data->questionattemptid = $this->get_new_parentid('question_attempt');
+ $data->timecreated = $this->apply_date_offset($data->timecreated);
+ $data->userid = $this->get_mappingid('user', $data->userid);
- // Note: question_sessions haven't files associated. On purpose manualcomment is lacking
- // support for them, so we don't need to handle them here.
+ // Everything ready, insert and create mapping (needed by question_sessions)
+ $newitemid = $DB->insert_record('question_attempt_steps', $data);
+ $this->set_mapping('question_attempt_step', $oldid, $newitemid, true);
+
+ // Now process the response data.
+ $qtyperestorer = $this->get_qtype_restorer($this->qtypes[$data->questionattemptid]);
+ if ($qtyperestorer) {
+ $response = $qtyperestorer->recode_response(
+ $this->newquestionids[$data->questionattemptid],
+ $data->sequencenumber, $response);
+ }
+ foreach ($response as $name => $value) {
+ $row = new stdClass();
+ $row->attemptstepid = $newitemid;
+ $row->name = $name;
+ $row->value = $value;
+ $DB->insert_record('question_attempt_step_data', $row, false);
+ }
}
/**
@@ -2581,25 +2683,33 @@ protected function questions_recode_layout($layout) {
}
/**
- * Given one question_states record, return the answer
- * recoded pointing to all the restored stuff
+ * Get the restore_qtype_plugin subclass for a specific question type.
+ * @param string $qtype e.g. multichoice.
+ * @return restore_qtype_plugin instance.
*/
- public function restore_recode_answer($state, $qtype) {
+ protected function get_qtype_restorer($qtype) {
// Build one static cache to store {@link restore_qtype_plugin}
// while we are needing them, just to save zillions of instantiations
// or using static stuff that will break our nice API
static $qtypeplugins = array();
- // If we haven't the corresponding restore_qtype_plugin for current qtype
- // instantiate it and add to cache
if (!isset($qtypeplugins[$qtype])) {
$classname = 'restore_qtype_' . $qtype . '_plugin';
if (class_exists($classname)) {
$qtypeplugins[$qtype] = new $classname('qtype', $qtype, $this);
} else {
- $qtypeplugins[$qtype] = false;
+ $qtypeplugins[$qtype] = null;
}
}
- return !empty($qtypeplugins[$qtype]) ? $qtypeplugins[$qtype]->recode_state_answer($state) : $state->answer;
+ return $qtypeplugins[$qtype];
+ }
+
+ protected function after_execute() {
+ parent::after_execute();
+
+ // Restore any files belonging to responses.
+ foreach (question_engine::get_all_response_file_areas() as $filearea) {
+ $this->add_related_files('question', $filearea, 'question_attempt_step');
+ }
}
}
View
73 enrol/externallib.php
@@ -43,9 +43,9 @@ public static function get_enrolled_users_parameters() {
return new external_function_parameters(
array(
'courseid' => new external_value(PARAM_INT, 'Course id'),
- 'withcapability' => new external_value(PARAM_CAPABILITY, 'User should have this capability'),
- 'groupid' => new external_value(PARAM_INT, 'Group id, null means all groups'),
- 'onlyactive' => new external_value(PARAM_INT, 'True means only active, false means all participants'),
+ 'withcapability' => new external_value(PARAM_CAPABILITY, 'User should have this capability', VALUE_DEFAULT, null),
+ 'groupid' => new external_value(PARAM_INT, 'Group id, null means all groups', VALUE_DEFAULT, null),
+ 'onlyactive' => new external_value(PARAM_INT, 'True means only active, false means all participants', VALUE_DEFAULT, 0),
)
);
}
@@ -59,12 +59,17 @@ public static function get_enrolled_users_parameters() {
* @param bool $onlyactive
* @return array of course participants
*/
- public static function get_enrolled_users($courseid, $withcapability, $groupid, $onlyactive) {
- global $DB;
+ public static function get_enrolled_users($courseid, $withcapability = null, $groupid = null, $onlyactive = false) {
+ global $DB, $CFG, $USER;
// Do basic automatic PARAM checks on incoming data, using params description
// If any problems are found then exceptions are thrown with helpful error messages
- $params = self::validate_parameters(self::get_enrolled_users_parameters(), array('courseid'=>$courseid, 'withcapability'=>$withcapability, 'groupid'=>$groupid, 'onlyactive'=>$onlyactive));
+ $params = self::validate_parameters(self::get_enrolled_users_parameters(), array(
+ 'courseid'=>$courseid,
+ 'withcapability'=>$withcapability,
+ 'groupid'=>$groupid,
+ 'onlyactive'=>$onlyactive)
+ );
$coursecontext = get_context_instance(CONTEXT_COURSE, $params['courseid']);
if ($courseid == SITEID) {
@@ -76,11 +81,10 @@ public static function get_enrolled_users($courseid, $withcapability, $groupid,
try {
self::validate_context($context);
} catch (Exception $e) {
- $exceptionparam = new stdClass();
- $exceptionparam->message = $e->getMessage();
- $exceptionparam->courseid = $params['courseid'];
- throw new moodle_exception(
- get_string('errorcoursecontextnotvalid' , 'webservice', $exceptionparam));
+ $exceptionparam = new stdClass();
+ $exceptionparam->message = $e->getMessage();
+ $exceptionparam->courseid = $params['courseid'];
+ throw new moodle_exception(get_string('errorcoursecontextnotvalid' , 'webservice', $exceptionparam));
}
if ($courseid == SITEID) {
@@ -92,28 +96,46 @@ public static function get_enrolled_users($courseid, $withcapability, $groupid,
if ($withcapability) {
require_capability('moodle/role:review', $coursecontext);
}
- if ($groupid) {
- if (groups_is_member($groupid)) {
- require_capability('moodle/site:accessallgroups', $coursecontext);
- }
+ if ($groupid && groups_is_member($groupid)) {
+ require_capability('moodle/site:accessallgroups', $coursecontext);
}
if ($onlyactive) {
require_capability('moodle/course:enrolreview', $coursecontext);
}
- list($sql, $params) = get_enrolled_sql($coursecontext, $withcapability, $groupid, $onlyactive);
- $sql = "SELECT DISTINCT ue.userid, e.courseid
+ list($sqlparams, $params) = get_enrolled_sql($coursecontext, $withcapability, $groupid, $onlyactive);
+ $sql = "SELECT ue.userid, e.courseid, u.firstname, u.lastname, u.username, c.id as usercontextid
FROM {user_enrolments} ue
JOIN {enrol} e ON (e.id = ue.enrolid)
- WHERE e.courseid = :courseid AND ue.userid IN ($sql)";
+ JOIN {user} u ON (ue.userid = u.id)
+ JOIN {context} c ON (u.id = c.instanceid AND contextlevel = " . CONTEXT_USER . ")
+ WHERE e.courseid = :courseid AND ue.userid IN ($sqlparams)
+ GROUP BY ue.userid, e.courseid, u.firstname, u.lastname, u.username, c.id";
$params['courseid'] = $courseid;
-
$enrolledusers = $DB->get_records_sql($sql, $params);
-
$result = array();
+ $isadmin = is_siteadmin($USER);
+ $canviewfullnames = has_capability('moodle/site:viewfullnames', $context);
foreach ($enrolledusers as $enrolleduser) {
- $result[] = array('courseid' => $enrolleduser->courseid,
- 'userid' => $enrolleduser->userid);
+ $profilimgurl = moodle_url::make_pluginfile_url($enrolleduser->usercontextid, 'user', 'icon', NULL, '/', 'f1');
+ $profilimgurlsmall = moodle_url::make_pluginfile_url($enrolleduser->usercontextid, 'user', 'icon', NULL, '/', 'f2');
+ $resultuser = array(
+ 'courseid' => $enrolleduser->courseid,
+ 'userid' => $enrolleduser->userid,
+ 'fullname' => fullname($enrolleduser),
+ 'profileimgurl' => $profilimgurl->out(false),
+ 'profileimgurlsmall' => $profilimgurlsmall->out(false)
+ );
+ // check if we can return username
+ if ($isadmin) {
+ $resultuser['username'] = $enrolleduser->username;
+ }
+ // check if we can return first and last name
+ if ($isadmin or $canviewfullnames) {
+ $resultuser['firstname'] = $enrolleduser->firstname;
+ $resultuser['lastname'] = $enrolleduser->lastname;
+ }
+ $result[] = $resultuser;
}
return $result;
@@ -129,12 +151,17 @@ public static function get_enrolled_users_returns() {
array(
'courseid' => new external_value(PARAM_INT, 'id of course'),
'userid' => new external_value(PARAM_INT, 'id of user'),
+ 'firstname' => new external_value(PARAM_RAW, 'first name of user', VALUE_OPTIONAL),
+ 'lastname' => new external_value(PARAM_RAW, 'last name of user', VALUE_OPTIONAL),
+ 'fullname' => new external_value(PARAM_RAW, 'fullname of user'),
+ 'username' => new external_value(PARAM_RAW, 'username of user', VALUE_OPTIONAL),
+ 'profileimgurl' => new external_value(PARAM_URL, 'url of the profile image'),
+ 'profileimgurlsmall' => new external_value(PARAM_URL, 'url of the profile image (small version)')
)
)
);
}
-
/**