Merge branch 'w18_MDL-32587_m23_phpunitindividual' of git://github.com/skodak/moodle
authorDan Poltawski <dan@moodle.com>
Thu, 3 May 2012 08:11:00 +0000 (16:11 +0800)
committerDan Poltawski <dan@moodle.com>
Thu, 3 May 2012 08:11:00 +0000 (16:11 +0800)
277 files changed:
admin/environment.xml
admin/message.php
admin/settings/courses.php
admin/tool/assignmentupgrade/batchupgrade.php [new file with mode: 0644]
admin/tool/assignmentupgrade/index.php [new file with mode: 0644]
admin/tool/assignmentupgrade/lang/en/tool_assignmentupgrade.php [new file with mode: 0644]
admin/tool/assignmentupgrade/lib.php [new file with mode: 0644]
admin/tool/assignmentupgrade/listnotupgraded.php [new file with mode: 0644]
admin/tool/assignmentupgrade/locallib.php [new file with mode: 0644]
admin/tool/assignmentupgrade/module.js [new file with mode: 0644]
admin/tool/assignmentupgrade/renderer.php [new file with mode: 0644]
admin/tool/assignmentupgrade/settings.php [new file with mode: 0644]
admin/tool/assignmentupgrade/styles.css [new file with mode: 0644]
admin/tool/assignmentupgrade/upgradableassignmentsbatchform.php [new file with mode: 0644]
admin/tool/assignmentupgrade/upgradableassignmentstable.php [new file with mode: 0644]
admin/tool/assignmentupgrade/upgradesingle.php [new file with mode: 0644]
admin/tool/assignmentupgrade/upgradesingleconfirm.php [new file with mode: 0644]
admin/tool/assignmentupgrade/version.php [new file with mode: 0644]
admin/tool/unittest/ex_reporter.php
backup/util/helper/backup_cron_helper.class.php
calendar/export_execute.php
calendar/renderer.php
calendar/set.php
calendar/view.php
config-dist.php
course/category.php
course/editcategory.php
course/externallib.php
course/format/topics/format.js [new file with mode: 0644]
course/format/topics/format.php
course/format/weeks/format.js [new file with mode: 0644]
course/format/weeks/format.php
course/lib.php
course/rest.php
course/tests/courselib_test.php
course/yui/dragdrop/dragdrop.js
enrol/authorize/db/messages.php [new file with mode: 0644]
enrol/authorize/version.php
enrol/externallib.php
enrol/flatfile/db/messages.php [new file with mode: 0644]
enrol/flatfile/lang/en/enrol_flatfile.php
enrol/flatfile/version.php
enrol/imsenterprise/db/messages.php [new file with mode: 0644]
enrol/imsenterprise/lang/en/enrol_imsenterprise.php
enrol/imsenterprise/version.php
enrol/manual/externallib.php
enrol/paypal/db/messages.php [new file with mode: 0644]
enrol/paypal/lang/en/enrol_paypal.php
enrol/paypal/version.php
files/externallib.php
grade/edit/tree/grade_form.php
grade/grading/form/guide/README [new file with mode: 0644]
grade/grading/form/guide/backup/moodle2/backup_gradingform_guide_plugin.class.php [new file with mode: 0644]
grade/grading/form/guide/backup/moodle2/restore_gradingform_guide_plugin.class.php [new file with mode: 0644]
grade/grading/form/guide/db/install.xml [new file with mode: 0644]
grade/grading/form/guide/edit.php [new file with mode: 0644]
grade/grading/form/guide/edit_form.php [new file with mode: 0644]
grade/grading/form/guide/guideeditor.php [new file with mode: 0644]
grade/grading/form/guide/js/guide.js [new file with mode: 0644]
grade/grading/form/guide/js/guideeditor.js [new file with mode: 0644]
grade/grading/form/guide/lang/en/gradingform_guide.php [new file with mode: 0644]
grade/grading/form/guide/lib.php [new file with mode: 0644]
grade/grading/form/guide/pix/icon.png [new file with mode: 0644]
grade/grading/form/guide/preview.php [new file with mode: 0644]
grade/grading/form/guide/renderer.php [new file with mode: 0644]
grade/grading/form/guide/styles.css [new file with mode: 0644]
grade/grading/form/guide/version.php [new file with mode: 0644]
grade/report/grader/index.php
grade/report/grader/lib.php
grade/report/grader/module.js
grade/report/user/lib.php
group/externallib.php
lang/en/admin.php
lang/en/grades.php
lang/en/message.php
lang/en/plugin.php
lib/configonlylib.php
lib/db/services.php
lib/db/upgradelib.php
lib/environmentlib.php
lib/externallib.php
lib/filelib.php
lib/filestorage/file_storage.php
lib/flickrlib.php
lib/googleapi.php
lib/javascript.php
lib/messagelib.php
lib/moodlelib.php
lib/navigationlib.php
lib/outputrenderers.php
lib/outputrequirementslib.php
lib/phpunit/bootstrap.php
lib/pluginlib.php
lib/setup.php
lib/setuplib.php
lib/tests/moodlelib_test.php
lib/tests/weblib_test.php
lib/upgradelib.php
lib/weblib.php
lib/xsendfilelib.php [new file with mode: 0644]
local/readme.txt
message/externallib.php
mod/assign/adminlib.php [new file with mode: 0644]
mod/assign/adminmanageplugins.php [new file with mode: 0644]
mod/assign/assignmentplugin.php [new file with mode: 0644]
mod/assign/backup/moodle2/backup_assign_activity_task.class.php [new file with mode: 0644]
mod/assign/backup/moodle2/backup_assign_stepslib.php [new file with mode: 0644]
mod/assign/backup/moodle2/restore_assign_activity_task.class.php [new file with mode: 0644]
mod/assign/backup/moodle2/restore_assign_stepslib.php [new file with mode: 0644]
mod/assign/db/access.php [new file with mode: 0644]
mod/assign/db/events.php [new file with mode: 0644]
mod/assign/db/install.xml [new file with mode: 0644]
mod/assign/db/log.php [new file with mode: 0644]
mod/assign/db/messages.php [new file with mode: 0644]
mod/assign/db/subplugins.php [new file with mode: 0644]
mod/assign/db/upgrade.php [new file with mode: 0644]
mod/assign/feedback/comments/backup/moodle2/backup_assignfeedback_comments_subplugin.class.php [new file with mode: 0644]
mod/assign/feedback/comments/backup/moodle2/restore_assignfeedback_comments_subplugin.class.php [new file with mode: 0644]
mod/assign/feedback/comments/db/access.php [new file with mode: 0644]
mod/assign/feedback/comments/db/install.php [new file with mode: 0644]
mod/assign/feedback/comments/db/install.xml [new file with mode: 0644]
mod/assign/feedback/comments/db/upgrade.php [new file with mode: 0644]
mod/assign/feedback/comments/lang/en/assignfeedback_comments.php [new file with mode: 0644]
mod/assign/feedback/comments/locallib.php [new file with mode: 0644]
mod/assign/feedback/comments/version.php [new file with mode: 0644]
mod/assign/feedback/file/backup/moodle2/backup_assignfeedback_file_subplugin.class.php [new file with mode: 0644]
mod/assign/feedback/file/backup/moodle2/restore_assignfeedback_file_subplugin.class.php [new file with mode: 0644]
mod/assign/feedback/file/db/access.php [new file with mode: 0644]
mod/assign/feedback/file/db/install.php [new file with mode: 0644]
mod/assign/feedback/file/db/install.xml [new file with mode: 0644]
mod/assign/feedback/file/db/upgrade.php [new file with mode: 0644]
mod/assign/feedback/file/lang/en/assignfeedback_file.php [new file with mode: 0644]
mod/assign/feedback/file/lib.php [new file with mode: 0644]
mod/assign/feedback/file/locallib.php [new file with mode: 0644]
mod/assign/feedback/file/version.php [new file with mode: 0644]
mod/assign/feedbackplugin.php [new file with mode: 0644]
mod/assign/gradeform.php [new file with mode: 0644]
mod/assign/gradingactionsform.php [new file with mode: 0644]
mod/assign/gradingbatchoperationsform.php [new file with mode: 0644]
mod/assign/gradingoptionsform.php [new file with mode: 0644]
mod/assign/gradingtable.php [new file with mode: 0644]
mod/assign/index.php [new file with mode: 0644]
mod/assign/lang/en/assign.php [new file with mode: 0644]
mod/assign/lib.php [new file with mode: 0644]
mod/assign/locallib.php [new file with mode: 0644]
mod/assign/mod_form.php [new file with mode: 0644]
mod/assign/module.js [new file with mode: 0644]
mod/assign/pix/gradefeedback.gif [new file with mode: 0644]
mod/assign/pix/icon.gif [new file with mode: 0644]
mod/assign/portfolio_callback.php [new file with mode: 0644]
mod/assign/renderable.php [new file with mode: 0644]
mod/assign/renderer.php [new file with mode: 0644]
mod/assign/settings.php [new file with mode: 0644]
mod/assign/styles.css [new file with mode: 0644]
mod/assign/submission/comments/db/access.php [new file with mode: 0644]
mod/assign/submission/comments/db/install.php [new file with mode: 0644]
mod/assign/submission/comments/db/upgrade.php [new file with mode: 0644]
mod/assign/submission/comments/lang/en/assignsubmission_comments.php [new file with mode: 0644]
mod/assign/submission/comments/lib.php [new file with mode: 0644]
mod/assign/submission/comments/locallib.php [new file with mode: 0644]
mod/assign/submission/comments/version.php [new file with mode: 0644]
mod/assign/submission/file/backup/moodle2/backup_assignsubmission_file_subplugin.class.php [new file with mode: 0644]
mod/assign/submission/file/backup/moodle2/restore_assignsubmission_file_subplugin.class.php [new file with mode: 0644]
mod/assign/submission/file/db/access.php [new file with mode: 0644]
mod/assign/submission/file/db/install.xml [new file with mode: 0644]
mod/assign/submission/file/db/upgrade.php [new file with mode: 0644]
mod/assign/submission/file/lang/en/assignsubmission_file.php [new file with mode: 0644]
mod/assign/submission/file/lib.php [new file with mode: 0644]
mod/assign/submission/file/locallib.php [new file with mode: 0644]
mod/assign/submission/file/settings.php [new file with mode: 0644]
mod/assign/submission/file/version.php [new file with mode: 0644]
mod/assign/submission/onlinetext/backup/moodle2/backup_assignsubmission_onlinetext_subplugin.class.php [new file with mode: 0644]
mod/assign/submission/onlinetext/backup/moodle2/restore_assignsubmission_onlinetext_subplugin.class.php [new file with mode: 0644]
mod/assign/submission/onlinetext/db/access.php [new file with mode: 0644]
mod/assign/submission/onlinetext/db/install.php [new file with mode: 0644]
mod/assign/submission/onlinetext/db/install.xml [new file with mode: 0644]
mod/assign/submission/onlinetext/db/upgrade.php [new file with mode: 0644]
mod/assign/submission/onlinetext/lang/en/assignsubmission_onlinetext.php [new file with mode: 0644]
mod/assign/submission/onlinetext/lib.php [new file with mode: 0644]
mod/assign/submission/onlinetext/locallib.php [new file with mode: 0644]
mod/assign/submission/onlinetext/version.php [new file with mode: 0644]
mod/assign/submission_form.php [new file with mode: 0644]
mod/assign/submissionplugin.php [new file with mode: 0644]
mod/assign/upgradelib.php [new file with mode: 0644]
mod/assign/version.php [new file with mode: 0644]
mod/assign/view.php [new file with mode: 0644]
mod/assignment/db/install.php [new file with mode: 0644]
mod/assignment/lang/en/assignment.php
mod/assignment/lib.php
mod/choice/view.php
mod/data/lib.php
mod/feedback/show_entries.php
mod/forum/lib.php
mod/forum/post.php
mod/forum/search.php
mod/forum/view.php
mod/quiz/accessmanager.php
mod/quiz/accessrule/openclosedate/rule.php
mod/quiz/accessrule/openclosedate/tests/rule_test.php
mod/quiz/attempt.php
mod/quiz/attemptlib.php
mod/quiz/backup/moodle2/backup_quiz_stepslib.php
mod/quiz/comment.php
mod/quiz/cronlib.php [new file with mode: 0644]
mod/quiz/db/access.php
mod/quiz/db/events.php
mod/quiz/db/install.xml
mod/quiz/db/messages.php
mod/quiz/db/upgrade.php
mod/quiz/lang/en/quiz.php
mod/quiz/lib.php
mod/quiz/locallib.php
mod/quiz/mod_form.php
mod/quiz/module.js
mod/quiz/processattempt.php
mod/quiz/renderer.php
mod/quiz/report/attemptsreport.php
mod/quiz/report/attemptsreport_form.php
mod/quiz/report/attemptsreport_options.php
mod/quiz/report/attemptsreport_table.php
mod/quiz/report/grading/report.php
mod/quiz/report/overview/lang/en/quiz_overview.php
mod/quiz/report/overview/overview_form.php
mod/quiz/report/overview/overview_options.php
mod/quiz/report/overview/overview_table.php
mod/quiz/report/overview/report.php
mod/quiz/report/reportlib.php
mod/quiz/report/responses/lang/en/quiz_responses.php
mod/quiz/report/responses/report.php
mod/quiz/report/responses/responses_form.php
mod/quiz/report/responses/responses_options.php
mod/quiz/report/responses/responses_table.php
mod/quiz/report/statistics/report.php
mod/quiz/report/statistics/responseanalysis.php
mod/quiz/review.php
mod/quiz/settings.php
mod/quiz/settingslib.php
mod/quiz/startattempt.php
mod/quiz/styles.css
mod/quiz/summary.php
mod/quiz/version.php
mod/quiz/view.php
mod/resource/backup/moodle1/lib.php
mod/scorm/api.php
mod/scorm/loadSCO.php
mod/scorm/loaddatamodel.php
mod/scorm/player.php
mod/workshop/allocation/manual/lib.php
mod/workshop/allocation/manual/renderer.php
mod/workshop/assessment.php
mod/workshop/db/access.php
mod/workshop/fileinfolib.php
mod/workshop/lang/en/workshop.php
mod/workshop/lib.php
mod/workshop/locallib.php
mod/workshop/renderer.php
mod/workshop/styles.css
mod/workshop/submission.php
mod/workshop/view.php
notes/externallib.php
question/editlib.php
question/type/calculated/questiontype.php
question/type/calculatedmulti/edit_calculatedmulti_form.php
question/type/calculatedmulti/question.php
report/participation/index.php
repository/filepicker.php
repository/picasa/lib.php
theme/base/style/course.css
theme/image.php
theme/javascript.php
theme/nimble/style/menu.css
theme/yui_combo.php
theme/yui_image.php
user/editlib.php
user/externallib.php
user/index.php
user/lib.php

index cd17818..4b9f456 100644 (file)
     </DATABASE>
     <PHP version="5.3.2" level="required">
     </PHP>
+    <PCREUNICODE level="optional">
+      <FEEDBACK>
+        <ON_CHECK message="pcreunicodewarning" />
+      </FEEDBACK>
+    </PCREUNICODE>
     <PHP_EXTENSIONS>
       <PHP_EXTENSION name="iconv" level="required">
         <FEEDBACK>
         </FEEDBACK>
       </PHP_SETTING>
     </PHP_SETTINGS>
-  </MOODLE>
+</MOODLE>
 </COMPATIBILITY_MATRIX>
index 8daf613..8e78fc3 100644 (file)
@@ -34,6 +34,10 @@ 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);
+$uninstall  = optional_param('uninstall', 0, PARAM_INT);
+$confirm  = optional_param('confirm', false, PARAM_BOOL);
+
+$headingtitle = get_string('managemessageoutputs', 'message');
 
 if (!empty($disable) && confirm_sesskey()) {
     if (!$processor = $DB->get_record('message_processors', array('id'=>$disable))) {
@@ -42,14 +46,37 @@ if (!empty($disable) && confirm_sesskey()) {
     $DB->set_field('message_processors', 'enabled', '0', array('id'=>$processor->id));      // Disable output
 }
 
-if (!empty($enable) && confirm_sesskey() ) {
+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) {
+if (!empty($uninstall) && confirm_sesskey()) {
+    echo $OUTPUT->header();
+    echo $OUTPUT->heading($headingtitle);
+
+    if (!$processor = $DB->get_record('message_processors', array('id'=>$uninstall))) {
+        print_error('outputdoesnotexist', 'message');
+    }
+
+    $processorname = get_string('pluginname', 'message_'.$processor->name);
+
+    if (!$confirm) {
+        echo $OUTPUT->confirm(get_string('processordeleteconfirm', 'message', $processorname), 'message.php?uninstall='.$processor->id.'&confirm=1', 'message.php');
+        echo $OUTPUT->footer();
+        exit;
+
+    } else {
+        message_processor_uninstall($processor->name);
+        $a->processor = $processorname;
+        $a->directory = $CFG->dirroot.'/message/output/'.$processor->name;
+        notice(get_string('processordeletefiles', 'message', $a), 'message.php');
+    }
+}
+
+if ($disable || $enable || $uninstall) {
     $url = new moodle_url('message.php');
     redirect($url);
 }
@@ -65,6 +92,6 @@ $messageoutputs = $renderer->manage_messageoutputs($processors);
 
 // Display the page
 echo $OUTPUT->header();
-echo $OUTPUT->heading(get_string('managemessageoutputs', 'message'));
+echo $OUTPUT->heading($headingtitle);
 echo $messageoutputs;
 echo $OUTPUT->footer();
\ No newline at end of file
index 543346b..92722eb 100644 (file)
@@ -161,6 +161,7 @@ if ($hassiteconfig
         400 => '400',
         500 => '500');
     $temp->add(new admin_setting_configselect('backup/backup_auto_keep', new lang_string('keep'), new lang_string('backupkeephelp'), 1, $keepoptoins));
+    $temp->add(new admin_setting_configcheckbox('backup/backup_shortname', new lang_string('backup_shortname', 'admin'), new lang_string('backup_shortnamehelp', 'admin'), 0));
 
     // Automated defaults section.
     $temp->add(new admin_setting_heading('automatedsettings', new lang_string('automatedsettings','backup'), ''));
diff --git a/admin/tool/assignmentupgrade/batchupgrade.php b/admin/tool/assignmentupgrade/batchupgrade.php
new file mode 100644 (file)
index 0000000..b62260e
--- /dev/null
@@ -0,0 +1,45 @@
+<?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/>.
+
+/**
+ * Script to show all the assignments that have not been upgraded after the main upgrade.
+ *
+ * @package    tool_assignmentupgrade
+ * @copyright  2012 NetSpot
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+require_once(dirname(__FILE__) . '/../../../config.php');
+require_once(dirname(__FILE__) . '/locallib.php');
+require_once(dirname(__FILE__) . '/upgradableassignmentstable.php');
+require_once(dirname(__FILE__) . '/upgradableassignmentsbatchform.php');
+require_once($CFG->libdir . '/adminlib.php');
+
+// admin_externalpage_setup calls require_login and checks moodle/site:config
+admin_externalpage_setup('assignmentupgrade', '', array(), tool_assignmentupgrade_url('batchupgrade'));
+$PAGE->navbar->add(get_string('batchupgrade', 'tool_assignmentupgrade'));
+
+$renderer = $PAGE->get_renderer('tool_assignmentupgrade');
+
+$confirm = required_param('confirm', PARAM_BOOL);
+if (!$confirm) {
+    print_error('invalidrequest');
+    die();
+}
+$result = tool_assignmentupgrade_upgrade_multiple_assignments(optional_param('upgradeall', 0, PARAM_BOOL),
+                                                explode(',', optional_param('selected', '', PARAM_TEXT)));
+
+echo $renderer->convert_multiple_assignments_result($result);
diff --git a/admin/tool/assignmentupgrade/index.php b/admin/tool/assignmentupgrade/index.php
new file mode 100644 (file)
index 0000000..bb7ff49
--- /dev/null
@@ -0,0 +1,50 @@
+<?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/>.
+
+/**
+ * This tool can upgrade old assignment activities to the new assignment activity type
+ *
+ * The upgrade can be done on any old assignment instance providing it is using one of the core
+ * assignment subtypes (online text, single upload, etc).
+ * The new assignment module was introduced in Moodle 2.3 and although it completely reproduces
+ * the features of the existing assignment type it wasn't designed to replace it entirely as there
+ * are many custom assignment types people use and it wouldn't be practical to try to convert them.
+ * Instead the existing assignment type will be left in core and people will be encouraged to
+ * use the new assignment type.
+ *
+ * This screen is the main entry-point to the plugin, it gives the admin a list
+ * of options available to them.
+ *
+ * @package    tool_assignmentupgrade
+ * @copyright  2012 NetSpot
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+require_once(dirname(__FILE__) . '/../../../config.php');
+require_once(dirname(__FILE__) . '/locallib.php');
+require_once($CFG->libdir . '/adminlib.php');
+
+// admin_externalpage_setup calls require_login and checks moodle/site:config
+admin_externalpage_setup('assignmentupgrade');
+
+$renderer = $PAGE->get_renderer('tool_assignmentupgrade');
+
+$actions = array();
+
+$header = get_string('pluginname', 'tool_assignmentupgrade');
+$actions[] = tool_assignmentupgrade_action::make('listnotupgraded');
+
+echo $renderer->index_page($header, $actions);
\ No newline at end of file
diff --git a/admin/tool/assignmentupgrade/lang/en/tool_assignmentupgrade.php b/admin/tool/assignmentupgrade/lang/en/tool_assignmentupgrade.php
new file mode 100644 (file)
index 0000000..3b3734b
--- /dev/null
@@ -0,0 +1,56 @@
+<?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/>.
+
+/**
+ * Strings for the assignment upgrade tool
+ *
+ * @package    tool_assignmentupgrade
+ * @copyright  2012 NetSpot
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+$string['areyousure'] = 'Are you sure?';
+$string['areyousuremessage'] = 'Are you sure you want to upgrade the assignment "{$a->name}"?';
+$string['assignmentid'] = 'Assignment ID';
+$string['assignmentnotfound'] = 'Assignment could not be found (id={$a})';
+$string['assignmenttype'] = 'Assignment type';
+$string['backtoindex'] = 'Back to index';
+$string['batchoperations'] = 'Batch operations';
+$string['batchupgrade'] = 'Upgrade multiple assignments';
+$string['confirmbatchupgrade'] = 'Confirm batch upgrade assignments';
+$string['conversioncomplete'] = 'Assignment converted';
+$string['conversionfailed'] = 'The assignment conversion was not successful. The log from the upgrade was: <br />{$a}';
+$string['listnotupgraded'] = 'List assignments that have not been upgraded';
+$string['listnotupgraded_desc'] = 'You can upgrade individual assignments from here';
+$string['noassignmentstoupgrade'] = 'There are no assignments that require upgrading';
+$string['notsupported'] = '';
+$string['notupgradedintro'] = 'This page lists the assignments created with an older version of Moodle that have not been upgraded to the new assignment module in Moodle 2.3. Not all assignments can be upgraded - if they were created with a custom assignment subtype, then that subtype will need to be upgraded to the new assignment plugin format in order to complete the upgrade.';
+$string['notupgradedtitle'] = 'Assignments not upgraded';
+$string['pluginname'] = 'Assignment upgrade helper';
+$string['select'] = 'Select';
+$string['submissions'] = 'Submissions';
+$string['supported'] = 'Upgrade';
+$string['unknown'] = 'Unknown';
+$string['upgradeassignmentsummary'] = 'Upgrade assignment: {$a->name} (Course: {$a->shortname})';
+$string['upgradeassignmentsuccess'] = 'Result: Upgrade successful';
+$string['upgradeassignmentfailed'] = 'Result: Upgrade failed. The log from the upgrade was: <br/><div class="tool_assignmentupgrade_upgradelog">{$a->log}</div>';
+$string['upgradable'] = 'Upgradable';
+$string['upgradeselected'] = 'Upgrade selected assignments';
+$string['upgradeselectedcount'] = 'Upgrade {$a} selected assignments?';
+$string['upgradeall'] = 'Upgrade all assignments';
+$string['upgradeallconfirm'] = 'Upgrade all assignments?';
+$string['upgradesingle'] = 'Upgrade single assignment';
+$string['viewcourse'] = 'View the course with the converted assignment';
diff --git a/admin/tool/assignmentupgrade/lib.php b/admin/tool/assignmentupgrade/lib.php
new file mode 100644 (file)
index 0000000..d5601fa
--- /dev/null
@@ -0,0 +1,59 @@
+<?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/>.
+
+/**
+ * Lib functions (cron) to automatically complete the assignment module upgrade if it was not done all at once during the main upgrade.
+ *
+ * @package    tool_assignmentupgrade
+ * @copyright  2012 NetSpot
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die;
+
+/**
+ * Standard cron function
+ */
+function tool_assignmentupgrade_cron() {
+    $settings = get_config('tool_assignmentupgrade');
+    if (empty($settings->cronenabled)) {
+        return;
+    }
+
+    mtrace('assignmentupgrade: tool_assignmentupgrade_cron() started at '. date('H:i:s'));
+    try {
+        tool_assignmentupgrade_process($settings);
+    } catch (Exception $e) {
+        mtrace('assignmentupgrade: tool_assignmentupgrade_cron() failed with an exception:');
+        mtrace($e->getMessage());
+    }
+    mtrace('assignmentupgrade: tool_assignmentupgrade_cron() finished at ' . date('H:i:s'));
+}
+
+/**
+ * This function does the cron process within the time range according to settings.
+ * This is not implemented yet
+ * @param stdClass $settings - not used
+ */
+function tool_assignmentupgrade_process($settings) {
+    global $CFG;
+    require_once(dirname(__FILE__) . '/locallib.php');
+
+    mtrace('assignmentupgrade: processing ...');
+
+    mtrace('assignmentupgrade: Done.');
+    return;
+}
diff --git a/admin/tool/assignmentupgrade/listnotupgraded.php b/admin/tool/assignmentupgrade/listnotupgraded.php
new file mode 100644 (file)
index 0000000..488ec6e
--- /dev/null
@@ -0,0 +1,48 @@
+<?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/>.
+
+/**
+ * Script to show all the assignments that have not been upgraded after the main upgrade.
+ *
+ * @package    tool_assignmentupgrade
+ * @copyright  2012 NetSpot
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+require_once(dirname(__FILE__) . '/../../../config.php');
+require_once(dirname(__FILE__) . '/locallib.php');
+require_once(dirname(__FILE__) . '/upgradableassignmentstable.php');
+require_once(dirname(__FILE__) . '/upgradableassignmentsbatchform.php');
+require_once($CFG->libdir . '/adminlib.php');
+
+// admin_externalpage_setup calls require_login and checks moodle/site:config
+admin_externalpage_setup('assignmentupgrade', '', array(), tool_assignmentupgrade_url('listnotupgraded'));
+$PAGE->navbar->add(get_string('listnotupgraded', 'tool_assignmentupgrade'));
+
+$renderer = $PAGE->get_renderer('tool_assignmentupgrade');
+
+$perpage = get_user_preferences('tool_assignmentupgrade_perpage', 5);
+$assignments = new tool_assignmentupgrade_assignments_table($perpage);
+
+$batchform = new tool_assignmentupgrade_batchoperations_form();
+$data = $batchform->get_data();
+if ($data && $data->selectedassignments != '' || $data && isset($data->upgradeall)) {
+    echo $renderer->confirm_batch_operation_page($data);
+} else {
+    echo $renderer->assignment_list_page($assignments, $batchform);
+}
+
+
diff --git a/admin/tool/assignmentupgrade/locallib.php b/admin/tool/assignmentupgrade/locallib.php
new file mode 100644 (file)
index 0000000..89e0e24
--- /dev/null
@@ -0,0 +1,245 @@
+<?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/>.
+
+/**
+ * Assignment upgrade tool library functions
+ *
+ * @package    tool_assignmentupgrade
+ * @copyright  2012 NetSpot
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Get the URL of a script within this plugin.
+ * @param string $script the script name, without .php. E.g. 'index'
+ * @param array $params URL parameters (optional)
+ * @return moodle_url
+ */
+function tool_assignmentupgrade_url($script, $params = array()) {
+    return new moodle_url('/admin/tool/assignmentupgrade/' . $script . '.php', $params);
+}
+
+/**
+ * Class to encapsulate the continue / cancel for batch operations
+ *
+ * @package    tool_assignmentupgrade
+ * @copyright  2012 NetSpot
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class tool_assignmentupgrade_batchoperationconfirm implements renderable {
+    /** @var string $continuemessage The message to show above the continue cancel buttons */
+    public $continuemessage = '';
+    /** @var string $continueurl The url to load if the user clicks continue */
+    public $continueurl;
+
+    /**
+     * Constructor for this class
+     * @param stdClass $data - The data from the previous batch form
+     */
+    function __construct($data) {
+        if (isset($data->upgradeselected)) {
+            $this->continuemessage = get_string('upgradeselectedcount', 'tool_assignmentupgrade', count(explode(',', $data->selectedassignments)));
+            $this->continueurl = new moodle_url('/admin/tool/assignmentupgrade/batchupgrade.php', array('upgradeselected'=>'1', 'confirm'=>'1', 'sesskey'=>sesskey(), 'selected'=>$data->selectedassignments));
+        } else if (isset($data->upgradeall)) {
+            if (!tool_assignmentupgrade_any_upgradable_assignments()) {
+                $this->continuemessage = get_string('noassignmentstoupgrade', 'tool_assignmentupgrade');
+                $this->continueurl = '';
+            } else {
+                $this->continuemessage = get_string('upgradeallconfirm', 'tool_assignmentupgrade');
+                $this->continueurl = new moodle_url('/admin/tool/assignmentupgrade/batchupgrade.php', array('upgradeall'=>'1', 'confirm'=>'1', 'sesskey'=>sesskey()));
+            }
+        }
+    }
+}
+
+
+/**
+ * Class to encapsulate one of the functionalities that this plugin offers.
+ *
+ * @package    tool_assignmentupgrade
+ * @copyright  2012 NetSpot
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class tool_assignmentupgrade_action {
+    /** @var string the name of this action. */
+    public $name;
+    /** @var moodle_url the URL to launch this action. */
+    public $url;
+    /** @var string a description of this aciton. */
+    public $description;
+
+    /**
+     * Constructor to set the fields.
+     *
+     * In order to create a new tool_assignmentupgrade_action instance you must use the tool_assignmentupgrade_action::make
+     * method.
+     *
+     * @param string $name the name of this action.
+     * @param moodle_url $url the URL to launch this action.
+     * @param string $description a description of this aciton.
+     */
+    protected function __construct($name, moodle_url $url, $description) {
+        $this->name = $name;
+        $this->url = $url;
+        $this->description = $description;
+    }
+
+    /**
+     * Make an action with standard values.
+     * @param string $shortname internal name of the action. Used to get strings and build a URL.
+     * @param array $params any URL params required.
+     * @return tool_assignmentupgrade_action
+     */
+    public static function make($shortname, $params = array()) {
+        return new self(
+                get_string($shortname, 'tool_assignmentupgrade'),
+                tool_assignmentupgrade_url($shortname, $params),
+                get_string($shortname . '_desc', 'tool_assignmentupgrade'));
+    }
+}
+
+/**
+ * Determine if there are any assignments that can be upgraded
+ * @return boolean - Are there any assignments that can be upgraded
+ */
+function tool_assignmentupgrade_any_upgradable_assignments() {
+    global $DB, $CFG;
+    require_once($CFG->dirroot . '/mod/assign/locallib.php');
+    // first find all the unique assignment types
+    $types = $DB->get_records_sql('SELECT plugin AS assignmenttype, value AS version FROM {config_plugins} WHERE name = ? AND plugin LIKE ?', array('version', 'assignment_%'));
+
+    $upgradabletypes = array();
+
+    foreach ($types as $assignment) {
+        $shorttype = substr($assignment->assignmenttype, strlen('assignment_'));
+        if (assign::can_upgrade_assignment($shorttype, $assignment->version)) {
+            $upgradabletypes[] = $shorttype;
+        }
+    }
+    $paramlist = '?';
+    foreach ($upgradabletypes as $index => $upgradabletype) {
+        if ($index > 0) {
+            $paramlist .= ', ?';
+        }
+    }
+
+    $record = $DB->get_record_sql('SELECT COUNT(id) as count from {assignment} where assignmenttype in (' . $paramlist . ')', $upgradabletypes);
+
+    return $record->count > 0;
+}
+
+/**
+ * Load a list of all the assignmentids that can be upgraded
+ * @return array of assignment ids
+ */
+function tool_assignmentupgrade_load_all_upgradable_assignmentids() {
+    global $DB, $CFG;
+    require_once($CFG->dirroot . '/mod/assign/locallib.php');
+    // first find all the unique assignment types
+    $types = $DB->get_records_sql('SELECT plugin AS assignmenttype, value AS version FROM {config_plugins} WHERE name = ? AND plugin LIKE ?', array('version', 'assignment_%'));
+
+    $upgradabletypes = array();
+
+    foreach ($types as $assignment) {
+        $shorttype = substr($assignment->assignmenttype, strlen('assignment_'));
+        if (assign::can_upgrade_assignment($shorttype, $assignment->version)) {
+            $upgradabletypes[] = $shorttype;
+        }
+    }
+    $paramlist = '?';
+    foreach ($upgradabletypes as $index => $upgradabletype) {
+        if ($index > 0) {
+            $paramlist .= ', ?';
+        }
+    }
+
+    $records = $DB->get_records_sql('SELECT id from {assignment} where assignmenttype in (' . $paramlist . ')', $upgradabletypes);
+    $ids = array();
+    foreach ($records as $record) {
+        $ids[] = $record->id;
+    }
+
+    return $ids;
+}
+
+
+/**
+ * Convert a list of assignments from the old format to the new one.
+ * @param bool $upgradeall - Upgrade all possible assignments
+ * @param array $assignmentids An array of assignment ids to upgrade
+ * @return array of $entry['assignmentsummary' => (result from tool_assignmentupgrade_get_assignment)
+ *                  $entry['success'] => boolean
+ *                  $entry['log'] => string - upgrade log
+ */
+function tool_assignmentupgrade_upgrade_multiple_assignments($upgradeall, $assignmentids) {
+    global $CFG;
+    require_once($CFG->dirroot . '/mod/assign/locallib.php');
+    require_once($CFG->dirroot . '/mod/assign/upgradelib.php');
+    $upgrades = array();
+
+    if ($upgradeall) {
+        $assignmentids = tool_assignmentupgrade_load_all_upgradable_assignmentids();
+    }
+
+    $assignment_upgrader = new assign_upgrade_manager();
+    foreach ($assignmentids as $assignmentid) {
+        $info = tool_assignmentupgrade_get_assignment($assignmentid);
+        if ($info) {
+            $log = '';
+            $success = $assignment_upgrader->upgrade_assignment($assignmentid, $log);
+        } else {
+            $success = false;
+            $log = get_string('assignmentnotfound', 'tool_assignmentupgrade', $assignmentid);
+            $info = new stdClass();
+            $info->name = get_string('unknown', 'tool_assignmentupgrade');
+            $info->shortname = get_string('unknown', 'tool_assignmentupgrade');
+        }
+
+        $upgrades[] = array('assignmentsummary'=>$info, 'success'=>$success, 'log'=>$log);
+    }
+    return $upgrades;
+}
+
+/**
+ * Convert a single assignment from the old format to the new one.
+ * @param stdClass $assignmentinfo An object containing information about this class
+ * @param string $log This gets appended to with the details of the conversion process
+ * @return boolean This is the overall result (true/false)
+ */
+function tool_assignmentupgrade_upgrade_assignment($assignmentinfo, &$log) {
+    global $CFG;
+    require_once($CFG->dirroot . '/mod/assign/locallib.php');
+    require_once($CFG->dirroot . '/mod/assign/upgradelib.php');
+    $assignment_upgrader = new assign_upgrade_manager();
+    return $assignment_upgrader->upgrade_assignment($assignmentinfo->id, $log);
+}
+
+/**
+ * Get the information about a assignment to be upgraded.
+ * @param int $assignmentid the assignment id.
+ * @return stdClass the information about that assignment.
+ */
+function tool_assignmentupgrade_get_assignment($assignmentid) {
+    global $DB;
+    return $DB->get_record_sql("
+            SELECT a.id, a.name, c.shortname, c.id AS courseid
+            FROM {assignment} a
+            JOIN {course} c ON c.id = a.course
+            WHERE a.id = ?", array($assignmentid));
+}
+
diff --git a/admin/tool/assignmentupgrade/module.js b/admin/tool/assignmentupgrade/module.js
new file mode 100644 (file)
index 0000000..829f99a
--- /dev/null
@@ -0,0 +1,66 @@
+
+M.tool_assignmentupgrade = {
+    init_upgrade_table: function(Y) {
+
+        Y.use('node', function(Y) {
+            checkboxes = Y.all('td.c0 input');
+            checkboxes.each(function(node) {
+                node.on('change', function(e) {
+                    rowelement = e.currentTarget.get('parentNode').get('parentNode');
+                    if (e.currentTarget.get('checked')) {
+                        rowelement.setAttribute('class', 'selectedrow');
+                    } else {
+                        rowelement.setAttribute('class', 'unselectedrow');
+                    }
+                });
+
+                rowelement = node.get('parentNode').get('parentNode');
+                if (node.get('checked')) {
+                    rowelement.setAttribute('class', 'selectedrow');
+                } else {
+                    rowelement.setAttribute('class', 'unselectedrow');
+                }
+            });
+        });
+
+        var selectall = Y.one('th.c0 input');
+        selectall.on('change', function(e) {
+            if (e.currentTarget.get('checked')) {
+                checkboxes = Y.all('td.c0 input');
+                checkboxes.each(function(node) {
+                    rowelement = node.get('parentNode').get('parentNode');
+                    node.set('checked', true);
+                    rowelement.setAttribute('class', 'selectedrow');
+                });
+            } else {
+                checkboxes = Y.all('td.c0 input');
+                checkboxes.each(function(node) {
+                    rowelement = node.get('parentNode').get('parentNode');
+                    node.set('checked', false);
+                    rowelement.setAttribute('class', 'unselectedrow');
+                });
+            }
+        });
+
+        var batchform = Y.one('.tool_assignmentupgrade_batchform form');
+        batchform.on('submit', function(e) {
+            checkboxes = Y.all('td.c0 input');
+            var selectedassignments = [];
+            checkboxes.each(function(node) {
+                if (node.get('checked')) {
+                    selectedassignments[selectedassignments.length] = node.get('value');
+                }
+            });
+
+            operation = Y.one('#id_operation');
+            assignmentsinput = Y.one('input.selectedassignments');
+            assignmentsinput.set('value', selectedassignments.join(','));
+            if (selectedassignments.length == 0) {
+                alert(M.str.assign.noassignmentsselected);
+                e.preventDefault();
+            }
+        });
+
+
+    }
+}
diff --git a/admin/tool/assignmentupgrade/renderer.php b/admin/tool/assignmentupgrade/renderer.php
new file mode 100644 (file)
index 0000000..16c013c
--- /dev/null
@@ -0,0 +1,278 @@
+<?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/>.
+
+/**
+ * Defines the renderer for the assignment upgrade helper plugin.
+ *
+ * @package    tool_assignmentupgrade
+ * @copyright  2012 NetSpot
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Renderer for the assignment upgrade helper plugin.
+ *
+ * @package    tool_assignmentupgrade
+ * @copyright  2012 NetSpot
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class tool_assignmentupgrade_renderer extends plugin_renderer_base {
+
+    /**
+     * Render the index page.
+     * @param string $detected information about what sort of site was detected.
+     * @param array $actions list of actions to show on this page.
+     * @return string html to output.
+     */
+    public function index_page($detected, array $actions) {
+        $output = '';
+        $output .= $this->header();
+        $output .= $this->heading(get_string('pluginname', 'tool_assignmentupgrade'));
+        $output .= $this->box($detected);
+        $output .= html_writer::start_tag('ul');
+        foreach ($actions as $action) {
+            $output .= html_writer::tag('li',
+                    html_writer::link($action->url, $action->name) . ' - ' .
+                    $action->description);
+        }
+        $output .= html_writer::end_tag('ul');
+        $output .= $this->footer();
+        return $output;
+    }
+
+    /**
+     * Render a page that is just a simple message.
+     * @param string $message the message to display.
+     * @return string html to output.
+     */
+    public function simple_message_page($message) {
+        $output = '';
+        $output .= $this->header();
+        $output .= $this->heading($message);
+        $output .= $this->back_to_index();
+        $output .= $this->footer();
+        return $output;
+    }
+
+    /**
+     * Render the confirm batch operation page
+     * @param stdClass $data Submitted form data with list of assignments to upgrade
+     * @return string html to output.
+     */
+    public function confirm_batch_operation_page(stdClass $data) {
+        $output = '';
+        $output .= $this->header();
+
+        $output .= $this->heading(get_string('confirmbatchupgrade', 'tool_assignmentupgrade'));
+        $output .= $this->output->spacer(array(), true);
+
+        $output .= $this->container_start('tool_assignmentupgrade_confirmbatch');
+
+        $output .= $this->render(new tool_assignmentupgrade_batchoperationconfirm($data));
+        $output .= $this->container_end();
+
+        $output .= $this->back_to_index();
+        $output .= $this->footer();
+        return $output;
+    }
+
+    /**
+     * Render the confirm batch continue / cancel links
+     * @param tool_assignmentupgrade_batchoperationconfirm $confirm Wrapper class to determine the continue message and url
+     * @return string html to output.
+     */
+    public function render_tool_assignmentupgrade_batchoperationconfirm(tool_assignmentupgrade_batchoperationconfirm $confirm) {
+        $output = '';
+
+        if ($confirm->continueurl) {
+            $output .= $this->output->confirm($confirm->continuemessage, $confirm->continueurl, tool_assignmentupgrade_url('listnotupgraded'));
+        } else {
+            $output .= $this->output->box($confirm->continuemessage);
+            $output .= $this->output->continue_button(tool_assignmentupgrade_url('listnotupgraded'));
+        }
+        return $output;
+    }
+
+    /**
+     * Render the list of assignments that still need to be upgraded page.
+     * @param tool_assignmentupgrade_assignments_table $assignments of data about assignments.
+     * @param tool_assignmentupgrade_batchoperations_form $batchform Submitted form with list of assignments to upgrade
+     * @return string html to output.
+     */
+    public function assignment_list_page(tool_assignmentupgrade_assignments_table $assignments, tool_assignmentupgrade_batchoperations_form $batchform) {
+        $output = '';
+        $output .= $this->header();
+        $this->page->requires->js_init_call('M.tool_assignmentupgrade.init_upgrade_table', array());
+
+
+        $output .= $this->heading(get_string('notupgradedtitle', 'tool_assignmentupgrade'));
+        $output .= $this->box(get_string('notupgradedintro', 'tool_assignmentupgrade'));
+        $output .= $this->output->spacer(array(), true);
+
+        $output .= $this->container_start('tool_assignmentupgrade_upgradetable');
+
+        $output .= $this->flexible_table($assignments, $assignments->get_rows_per_page(), true);
+        $output .= $this->container_end();
+
+        if ($assignments->anyupgradableassignments) {
+            $output .= $this->container_start('tool_assignmentupgrade_batchform');
+            $output .= $this->moodleform($batchform);
+            $output .= $this->container_end();
+        }
+
+        $output .= $this->back_to_index();
+        $output .= $this->footer();
+        return $output;
+    }
+
+    /**
+     * Render the result of an assignment conversion
+     * @param array $assignments - An array of arrays with keys $entry['assignmentsummary', 'success', 'log']
+     *                            See convert_assignment_result for more description of these keys.
+     * @return string html to output.
+     */
+    public function convert_multiple_assignments_result($assignments) {
+        $output = '';
+        $output .= $this->header();
+        $output .= $this->heading(get_string('batchupgrade', 'tool_assignmentupgrade'));
+
+        foreach ($assignments as $assignment) {
+            $assignmentsummary = $assignment['assignmentsummary'];
+            $success = $assignment['success'];
+            $log = $assignment['log'];
+
+            $output .= $this->heading(get_string('upgradeassignmentsummary', 'tool_assignmentupgrade', $assignmentsummary), 5);
+            if ($success) {
+                $output .= $this->container(get_string('upgradeassignmentsuccess', 'tool_assignmentupgrade'));
+
+            } else {
+                $output .= $this->container(get_string('upgradeassignmentfailed', 'tool_assignmentupgrade', $assignment));
+            }
+            if (isset($assignmentsummary->courseid)) {
+                $output .= html_writer::link(new moodle_url('/course/view.php', array('id'=>$assignmentsummary->courseid)) ,get_string('viewcourse', 'tool_assignmentupgrade'));
+            }
+
+
+        }
+
+        $output .= $this->continue_button(tool_assignmentupgrade_url('listnotupgraded'));
+
+
+        $output .= $this->footer();
+        return $output;
+    }
+
+    /**
+     * Render the result of an assignment conversion
+     * @param stdClass $assignmentsummary data about the assignment to upgrade.
+     * @param bool $success Set to true if the outcome of the conversion was a success
+     * @param string $log The log from the conversion
+     * @return string html to output.
+     */
+    public function convert_assignment_result($assignmentsummary, $success, $log) {
+        $output = '';
+        $output .= $this->header();
+        $output .= $this->heading(get_string('conversioncomplete', 'tool_assignmentupgrade'));
+
+        if (!$success) {
+            $output .= get_string('conversionfailed', 'tool_assignmentupgrade', $log);
+        } else {
+            $output .= html_writer::link(new moodle_url('/course/view.php', array('id'=>$assignmentsummary->courseid)) ,get_string('viewcourse', 'tool_assignmentupgrade'));
+        }
+
+        $output .= $this->continue_button(tool_assignmentupgrade_url('listnotupgraded'));
+
+
+        $output .= $this->footer();
+        return $output;
+    }
+
+    /**
+     * Render the are-you-sure page to confirm a manual upgrade.
+     * @param stdClass $assignmentsummary data about the assignment to upgrade.
+     * @return string html to output.
+     */
+    public function convert_assignment_are_you_sure($assignmentsummary) {
+        $output = '';
+        $output .= $this->header();
+        $output .= $this->heading(get_string('areyousure', 'tool_assignmentupgrade'));
+
+        $params = array('id' => $assignmentsummary->id, 'confirmed' => 1, 'sesskey' => sesskey());
+        $output .= $this->confirm(get_string('areyousuremessage', 'tool_assignmentupgrade', $assignmentsummary),
+                new single_button(tool_assignmentupgrade_url('upgradesingle', $params), get_string('yes')),
+                tool_assignmentupgrade_url('listnotupgraded'));
+
+        $output .= $this->footer();
+        return $output;
+    }
+
+    /**
+     * Helper method dealing with the fact we can not just fetch the output of flexible_table
+     *
+     * @param flexible_table $table
+     * @param int $rowsperpage
+     * @param bool $displaylinks Show links in the table
+     * @return string HTML
+     */
+    protected function flexible_table(flexible_table $table, $rowsperpage, $displaylinks) {
+
+        $o = '';
+        ob_start();
+        $table->out($rowsperpage, $displaylinks);
+        $o = ob_get_contents();
+        ob_end_clean();
+
+        return $o;
+    }
+
+    /**
+     * Helper method dealing with the fact we can not just fetch the output of moodleforms
+     *
+     * @param moodleform $mform
+     * @return string HTML
+     */
+    protected function moodleform(moodleform $mform) {
+
+        $o = '';
+        ob_start();
+        $mform->display();
+        $o = ob_get_contents();
+        ob_end_clean();
+
+        return $o;
+    }
+
+
+    /**
+     * Render a link in a div, such as the 'Back to plugin main page' link.
+     * @param string|moodle_url $url the link URL.
+     * @param string $text the link text.
+     * @return string html to output.
+     */
+    public function end_of_page_link($url, $text) {
+        return html_writer::tag('div', html_writer::link($url, $text), array('class' => 'mdl-align'));
+    }
+
+    /**
+     * Output a link back to the plugin index page.
+     * @return string html to output.
+     */
+    public function back_to_index() {
+        return $this->end_of_page_link(tool_assignmentupgrade_url('index'), get_string('backtoindex', 'tool_assignmentupgrade'));
+    }
+}
diff --git a/admin/tool/assignmentupgrade/settings.php b/admin/tool/assignmentupgrade/settings.php
new file mode 100644 (file)
index 0000000..a637e38
--- /dev/null
@@ -0,0 +1,31 @@
+<?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/>.
+
+/**
+ * Adds this plugin to the admin menu.
+ *
+ * @package    tool_assignmentupgrade
+ * @copyright  2012 NetSpot
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die;
+
+if ($hassiteconfig) { // needs this condition or there is error on login page
+    $ADMIN->add('root', new admin_externalpage('assignmentupgrade',
+            get_string('pluginname', 'tool_assignmentupgrade'),
+            new moodle_url('/admin/tool/assignmentupgrade/index.php')));
+}
\ No newline at end of file
diff --git a/admin/tool/assignmentupgrade/styles.css b/admin/tool/assignmentupgrade/styles.css
new file mode 100644 (file)
index 0000000..0188dc4
--- /dev/null
@@ -0,0 +1,11 @@
+#page-admin-tool-assignmentupgrade-listnotupgraded .tool_assignmentupgrade_upgradetable .c0 { display: none; }
+#page-admin-tool-assignmentupgrade-listnotupgraded.jsenabled .tool_assignmentupgrade_upgradetable .c0 { display: table-cell; }
+/*
+.gradingbatchoperationsform { display: none; }
+.jsenabled .gradingbatchoperationsform { display: block; }
+*/
+
+#page-admin-tool-assignmentupgrade-listnotupgraded .tool_assignmentupgrade_upgradetable tr.selectedrow td { background-color: #ffeecc; }
+#page-admin-tool-assignmentupgrade-listnotupgraded .tool_assignmentupgrade_upgradetable tr.unselectedrow td { background-color: white; }
+
+
diff --git a/admin/tool/assignmentupgrade/upgradableassignmentsbatchform.php b/admin/tool/assignmentupgrade/upgradableassignmentsbatchform.php
new file mode 100644 (file)
index 0000000..105530a
--- /dev/null
@@ -0,0 +1,55 @@
+<?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/>.
+
+/**
+ * This file contains the forms to create and edit an instance of this module
+ *
+ * @package   tool_assignmentupgrade
+ * @copyright 2012 NetSpot {@link http://www.netspot.com.au}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die('Direct access to this script is forbidden.');
+
+
+/** Include formslib.php */
+require_once ($CFG->libdir.'/formslib.php');
+
+/**
+ * Assignment upgrade batch operations form
+ *
+ * @package   tool_assignmentupgrade
+ * @copyright 2012 NetSpot {@link http://www.netspot.com.au}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class tool_assignmentupgrade_batchoperations_form extends moodleform {
+    /**
+     * Define this form - is called from parent constructor
+     */
+    function definition() {
+        $mform = $this->_form;
+        $instance = $this->_customdata;
+
+        $mform->addElement('header', 'general', get_string('batchoperations', 'tool_assignmentupgrade'));
+        // visible elements
+        $mform->addElement('hidden', 'selectedassignments', '', array('class'=>'selectedassignments'));
+
+        $mform->addElement('submit', 'upgradeselected', get_string('upgradeselected', 'tool_assignmentupgrade'));
+        $mform->addElement('submit', 'upgradeall', get_string('upgradeall', 'tool_assignmentupgrade'));
+    }
+
+}
+
diff --git a/admin/tool/assignmentupgrade/upgradableassignmentstable.php b/admin/tool/assignmentupgrade/upgradableassignmentstable.php
new file mode 100644 (file)
index 0000000..ed830f6
--- /dev/null
@@ -0,0 +1,172 @@
+<?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/>.
+
+/**
+ * This file contains the definition for the grading table which subclassses easy_table
+ *
+ * @package   tool_assignmentupgrade
+ * @copyright 2012 NetSpot {@link http://www.netspot.com.au}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+require_once($CFG->libdir.'/tablelib.php');
+require_once($CFG->libdir.'/gradelib.php');
+require_once($CFG->dirroot.'/mod/assign/locallib.php');
+
+/**
+ * Extends table_sql to provide a table of assignment submissions
+ *
+ * @package   tool_assignmentupgrade
+ * @copyright 2012 NetSpot {@link http://www.netspot.com.au}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class tool_assignmentupgrade_assignments_table extends table_sql implements renderable {
+    /** @var int $perpage */
+    private $perpage = 10;
+    /** @var int $rownum (global index of current row in table) */
+    private $rownum = -1;
+    /** @var renderer_base for getting output */
+    private $output = null;
+    /** @var boolean anyupgradableassignments - True if there is one or more assignments that can upgraded */
+    public $anyupgradableassignments = false;
+
+    /**
+     * This table loads a list of the old assignment instances and tests them to see if they can be upgraded
+     *
+     * @param int $perpage How many per page
+     * @param int $rowoffset The starting row for pagination
+     */
+    function __construct($perpage, $rowoffset=0) {
+        global $PAGE;
+        parent::__construct('tool_assignmentupgrade_assignments');
+        $this->perpage = $perpage;
+        $this->output = $PAGE->get_renderer('tool_assignmentupgrade');
+
+        $this->define_baseurl(new moodle_url('/admin/tool/assignmentupgrade/listnotupgraded.php'));
+
+        $this->anyupgradableassignments = tool_assignmentupgrade_any_upgradable_assignments();
+
+        // do some business - then set the sql
+        if ($rowoffset) {
+            $this->rownum = $rowoffset - 1;
+        }
+
+        $fields = 'a.id as id, a.name as name, a.assignmenttype as type, c.shortname as courseshortname, c.id as courseid, COUNT(s.id) as submissioncount';
+        $from = '{assignment} a JOIN {course} c ON a.course = c.id ' .
+                        ' LEFT JOIN {assignment_submissions} s ON a.id = s.assignment';
+
+
+        $where = '1 = 1';
+        $where .= ' GROUP BY a.id, a.name, a.assignmenttype, c.shortname, c.id ';
+
+        $this->set_sql($fields, $from, $where, array());
+        $this->set_count_sql('SELECT COUNT(*) FROM ' . $from, array());
+
+        $columns = array();
+        $headers = array();
+
+        $columns[] = 'select';
+        $headers[] = get_string('select', 'tool_assignmentupgrade') . '<div class="selectall"><input type="checkbox" name="selectall" title="' . get_string('selectall') . '"/></div>';
+        $columns[] = 'upgradable';
+        $headers[] = get_string('upgradable', 'tool_assignmentupgrade');
+        $columns[] = 'id';
+        $headers[] = get_string('assignmentid', 'tool_assignmentupgrade');
+        $columns[] = 'courseshortname';
+        $headers[] = get_string('course');
+        $columns[] = 'name';
+        $headers[] = get_string('name');
+        $columns[] = 'type';
+        $headers[] = get_string('assignmenttype', 'tool_assignmentupgrade');
+        $columns[] = 'submissioncount';
+        $headers[] = get_string('submissions', 'tool_assignmentupgrade');
+
+        // set the columns
+        $this->define_columns($columns);
+        $this->define_headers($headers);
+        $this->no_sorting('upgradable');
+        $this->no_sorting('select');
+    }
+
+    /**
+     * Return the number of rows to display on a single page
+     *
+     * @return int The number of rows per page
+     */
+    function get_rows_per_page() {
+        return $this->perpage;
+    }
+
+    /**
+     * Format a link to the assignment instance
+     *
+     * @param stdClass $row
+     * @return string
+     */
+    function col_name(stdClass $row) {
+        return html_writer::link(new moodle_url('/mod/assignment/view.php',
+                array('a' => $row->id)), $row->name);
+    }
+
+
+    /**
+     * Format a link to the upgrade single tool
+     *
+     * @param stdClass $row (contains cached result from previous upgradable check)
+     * @return string
+     */
+    function col_upgradable(stdClass $row) {
+        if ($row->upgradable) {
+            return html_writer::link(new moodle_url('/admin/tool/assignmentupgrade/upgradesingleconfirm.php',
+                    array('id' => $row->id)), get_string('supported', 'tool_assignmentupgrade'));
+        } else {
+            return get_string('notsupported', 'tool_assignmentupgrade');
+        }
+    }
+
+    /**
+     * Insert a checkbox for selecting the current row for batch operations
+     *
+     * @param stdClass $row
+     * @return string
+     */
+    function col_select(stdClass $row) {
+        global $CFG;
+        $version = get_config('assignment_' . $row->type, 'version');
+        require_once($CFG->dirroot . '/mod/assign/locallib.php');
+        if (assign::can_upgrade_assignment($row->type, $version)) {
+            $row->upgradable = true;
+            return '<input type="checkbox" name="selectedassignment" value="' . $row->id . '"/>';
+        }
+        $row->upgradable = false;
+        return '';
+    }
+
+    /**
+     * Override the table show_hide_link to not show for select column
+     *
+     * @param string $column the column name, index into various names.
+     * @param int $index numerical index of the column.
+     * @return string HTML fragment.
+     */
+    protected function show_hide_link($column, $index) {
+        if ($index > 0) {
+            return parent::show_hide_link($column, $index);
+        }
+        return '';
+    }
+}
diff --git a/admin/tool/assignmentupgrade/upgradesingle.php b/admin/tool/assignmentupgrade/upgradesingle.php
new file mode 100644 (file)
index 0000000..8ae2aab
--- /dev/null
@@ -0,0 +1,46 @@
+<?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/>.
+
+/**
+ * Script to show all the assignments that have not been upgraded after the main upgrade.
+ *
+ * @package    tool_assignmentupgrade
+ * @copyright  2012 NetSpot
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+require_once(dirname(__FILE__) . '/../../../config.php');
+require_once(dirname(__FILE__) . '/locallib.php');
+require_once($CFG->libdir . '/adminlib.php');
+
+$assignmentid = required_param('id', PARAM_INT);
+
+// admin_externalpage_setup calls require_login and checks moodle/site:config
+admin_externalpage_setup('assignmentupgrade', '', array(), tool_assignmentupgrade_url('upgradesingle', array('id' => $assignmentid)));
+
+$PAGE->navbar->add(get_string('upgradesingle', 'tool_assignmentupgrade'));
+$renderer = $PAGE->get_renderer('tool_assignmentupgrade');
+
+$assignmentinfo = tool_assignmentupgrade_get_assignment($assignmentid);
+if (!$assignmentinfo) {
+    print_error('invalidrequest');
+    die();
+}
+
+$log = '';
+$result = tool_assignmentupgrade_upgrade_assignment($assignmentinfo, $log);
+
+echo $renderer->convert_assignment_result($assignmentinfo, $result, $log);
diff --git a/admin/tool/assignmentupgrade/upgradesingleconfirm.php b/admin/tool/assignmentupgrade/upgradesingleconfirm.php
new file mode 100644 (file)
index 0000000..015d217
--- /dev/null
@@ -0,0 +1,39 @@
+<?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/>.
+
+/**
+ * Script to show all the assignments that have not been upgraded after the main upgrade.
+ *
+ * @package    tool_assignmentupgrade
+ * @copyright  2012 NetSpot
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+require_once(dirname(__FILE__) . '/../../../config.php');
+require_once(dirname(__FILE__) . '/locallib.php');
+require_once($CFG->libdir . '/adminlib.php');
+
+$assignmentid = required_param('id', PARAM_INT);
+
+// admin_externalpage_setup calls require_login and checks moodle/site:config
+admin_externalpage_setup('assignmentupgrade', '', array(), tool_assignmentupgrade_url('upgradesingle', array('id' => $assignmentid)));
+
+$PAGE->navbar->add(get_string('upgradesingle', 'tool_assignmentupgrade'));
+$renderer = $PAGE->get_renderer('tool_assignmentupgrade');
+
+$assignmentinfo = tool_assignmentupgrade_get_assignment($assignmentid);
+
+echo $renderer->convert_assignment_are_you_sure($assignmentinfo);
diff --git a/admin/tool/assignmentupgrade/version.php b/admin/tool/assignmentupgrade/version.php
new file mode 100644 (file)
index 0000000..6441ffd
--- /dev/null
@@ -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/>.
+
+/**
+ * Version details.
+ *
+ * @package    tool_assignmentupgrade
+ * @copyright  2012 NetSpot
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+$plugin->version   = 2012021700;
+$plugin->requires  = 2011092100;
+$plugin->component = 'tool_assignmentupgrade';
\ No newline at end of file
index b7b296c..01476d2 100644 (file)
@@ -147,11 +147,11 @@ class ExHtmlReporter extends HtmlReporter {
      * Private method. Used by printPass/Fail/Skip/Error.
      */
     function _paintPassFail($passorfail, $message, $stacktrace = null, $debuginfo = null) {
-        global $FULLME, $CFG, $OUTPUT;
+        global $CFG, $OUTPUT;
 
         echo $OUTPUT->box_start($passorfail . ' generalbox ');
 
-        $url = $this->_htmlEntities($this->_stripParameterFromUrl($FULLME, 'path'));
+        $url = $this->_htmlEntities($this->_stripParameterFromUrl(qualified_me(), 'path'));
         echo '<b class="', $passorfail, '">', $this->get_string($passorfail), '</b>: ';
         $breadcrumb = $this->getTestList();
         array_shift($breadcrumb);
index 32ab61e..d25c794 100644 (file)
@@ -356,7 +356,7 @@ abstract class backup_cron_automated_helper {
                 $dir = null;
             }
             if (!empty($dir) && $storage !== 0) {
-                $filename = backup_plan_dbops::get_default_backup_filename($format, $type, $course->id, $users, $anonymised, true);
+                $filename = backup_plan_dbops::get_default_backup_filename($format, $type, $course->id, $users, $anonymised, !$config->backup_shortname);
                 $outcome = $file->copy_content_to($dir.'/'.$filename);
                 if ($outcome && $storage === 1) {
                     $file->delete();
index ee0bed8..2298682 100644 (file)
@@ -174,11 +174,6 @@ if(empty($serialized)) {
     die('bad serialization');
 }
 
-//IE compatibility HACK!
-if (ini_get_bool('zlib.output_compression')) {
-    ini_set('zlib.output_compression', 'Off');
-}
-
 $filename = 'icalexport.ics';
 
 header('Last-Modified: '. gmdate('D, d M Y H:i:s', time()) .' GMT');
index 4fe8301..a2f8a7b 100644 (file)
@@ -135,7 +135,9 @@ class core_calendar_renderer extends plugin_renderer_base {
      * @return string
      */
     public function fake_block_filters($courseid, $day, $month, $year, $view, $courses) {
-        return html_writer::tag('div', calendar_filter_controls($this->page->url), array('class'=>'calendar_filters filters'));
+        $returnurl = $this->page->url;
+        $returnurl->param('course', $courseid);
+        return html_writer::tag('div', calendar_filter_controls($returnurl), array('class'=>'calendar_filters filters'));
     }
 
     /**
index 2633369..c02069e 100644 (file)
@@ -45,8 +45,13 @@ require_sesskey();
 
 $var = required_param('var', PARAM_ALPHA);
 $return = clean_param(base64_decode(required_param('return', PARAM_RAW)), PARAM_URL);
-
-$url = new moodle_url('/calendar/set.php', array('return'=>base64_encode($return),'var'=>$var, 'sesskey'=>sesskey()));
+$courseid = optional_param('id', -1, PARAM_INT);
+if ($courseid != -1) {
+    $return = new moodle_url($return, array('course' => $courseid));
+} else {
+    $return = new moodle_url($return);
+}
+$url = new moodle_url('/calendar/set.php', array('return'=>base64_encode($return->out(false)), 'course' => $courseid, 'var'=>$var, 'sesskey'=>sesskey()));
 $PAGE->set_url($url);
 $PAGE->set_context(get_context_instance(CONTEXT_SYSTEM));
 
index 31c3ee2..fbad9e0 100644 (file)
@@ -127,7 +127,7 @@ switch($view) {
         echo $renderer->show_day($calendar);
     break;
     case 'month':
-        echo $renderer->show_month_detailed($calendar);
+        echo $renderer->show_month_detailed($calendar, $url);
     break;
     case 'upcoming':
         $defaultlookahead = CALENDAR_DEFAULT_UPCOMING_LOOKAHEAD;
index 495319c..4b3534f 100644 (file)
@@ -220,6 +220,22 @@ $CFG->admin = 'admin';
 // about students being served outdated versions of uploaded files.
 //     $CFG->filelifetime = 86400;
 //
+// Some web servers can offload the file serving from PHP process,
+// comment out one the following options to enable it in Moodle:
+//     $CFG->xsendfile = 'X-Sendfile';           // Apache {@see https://tn123.org/mod_xsendfile/}
+//     $CFG->xsendfile = 'X-LIGHTTPD-send-file'; // Lighttpd {@see http://redmine.lighttpd.net/projects/lighttpd/wiki/X-LIGHTTPD-send-file}
+//     $CFG->xsendfile = 'X-Accel-Redirect';     // Nginx {@see http://wiki.nginx.org/XSendfile}
+// If your X-Sendfile implementation (usually Nginx) uses directory aliases specify them
+// in the following array setting:
+//     $CFG->xsendfilealiases = array(
+//         '/dataroot/' => $CFG->dataroot,
+//         '/cachedir/' => '/var/www/moodle/cache',    // for custom $CFG->cachedir locations
+//         '/tempdir/'  => '/var/www/moodle/temp',     // for custom $CFG->tempdir locations
+//         '/filedir'   => '/var/www/moodle/filedir',  // for custom $CFG->filedir locations
+//     );
+//
+//
+//
 // This setting will prevent the 'My Courses' page being displayed when a student
 // logs in. The site front page will always show the same (logged-out) view.
 //     $CFG->disablemycourses = true;
index 6f4f57c..935b83a 100644 (file)
@@ -131,10 +131,8 @@ if ($editingon && $sesskeyprovided) {
         if ($course) {
             $coursecontext = get_context_instance(CONTEXT_COURSE, $course->id);
             require_capability('moodle/course:visibility', $coursecontext);
-            // Set the visibility of the course
-            $DB->set_field('course', 'visible', $visible, array('id' => $course->id));
-            // we set the old flag when user manually changes visibility of course
-            $DB->set_field('course', 'visibleold', $visible, array('id' => $course->id));
+            // Set the visibility of the course. we set the old flag when user manually changes visibility of course.
+            $DB->update_record('course', array('id' => $course->id, 'visible' => $visible, 'visibleold' => $visible, 'timemodified' => time()));
         }
     }
 
index ff4bad5..97f791a 100644 (file)
@@ -114,11 +114,11 @@ if ($mform->is_cancelled()) {
     } else {
         // Create a new category.
         $newcategory->description = $data->description_editor['text'];
-        $newcategory->sortorder = 999;
-        $newcategory->id = $DB->insert_record('course_categories', $newcategory);
-        $newcategory->context = get_context_instance(CONTEXT_COURSECAT, $newcategory->id);
-        $categorycontext = $newcategory->context;
-        mark_context_dirty($newcategory->context->path);
+
+        // Don't overwrite the $newcategory object as it'll be processed by file_postupdate_standard_editor in a moment
+        $category = create_course_category($newcategory);
+        $newcategory->id = $category->id;
+        $categorycontext = $category->context;
     }
 
     $newcategory = file_postupdate_standard_editor($newcategory, 'description', $editoroptions, $categorycontext, 'coursecat', 'description', 0);
index e6a86ea..fef3647 100644 (file)
@@ -1,5 +1,4 @@
 <?php
-
 // This file is part of Moodle - http://moodle.org/
 //
 // Moodle is free software: you can redistribute it and/or modify
 // You should have received a copy of the GNU General Public License
 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
+
 /**
  * External course API
  *
- * @package    core
- * @subpackage course
- * @copyright  2010 Moodle Pty Ltd (http://moodle.com)
+ * @package    core_course
+ * @category   external
+ * @copyright  2009 Petr Skodak
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
@@ -29,13 +29,21 @@ defined('MOODLE_INTERNAL') || die;
 require_once("$CFG->libdir/externallib.php");
 
 /**
- * Course functions
+ * Course external functions
+ *
+ * @package    core_course
+ * @category   external
+ * @copyright  2011 Jerome Mouneyrac
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since Moodle 2.2
  */
 class core_course_external extends external_api {
 
     /**
      * Returns description of method parameters
+     *
      * @return external_function_parameters
+     * @since Moodle 2.2
      */
     public static function get_course_contents_parameters() {
         return new external_function_parameters(
@@ -52,9 +60,11 @@ class core_course_external extends external_api {
 
     /**
      * Get course contents
-     * @param int $courseid
-     * @param array $options, not used yet, might be used in later version
+     *
+     * @param int $courseid course id
+     * @param array $options These options are not used yet, might be used in later version
      * @return array
+     * @since Moodle 2.2
      */
     public static function get_course_contents($courseid, $options) {
         global $CFG, $DB;
@@ -183,7 +193,9 @@ class core_course_external extends external_api {
 
     /**
      * Returns description of method result value
+     *
      * @return external_description
+     * @since Moodle 2.2
      */
     public static function get_course_contents_returns() {
         return new external_multiple_structure(
@@ -238,7 +250,9 @@ class core_course_external extends external_api {
 
     /**
      * Returns description of method parameters
+     *
      * @return external_function_parameters
+     * @since Moodle 2.2
      */
     public static function get_courses_parameters() {
         return new external_function_parameters(
@@ -255,8 +269,10 @@ class core_course_external extends external_api {
 
     /**
      * Get courses
-     * @param array $options
+     *
+     * @param array $options It contains an array (list of ids)
      * @return array
+     * @since Moodle 2.2
      */
     public static function get_courses($options) {
         global $CFG, $DB;
@@ -336,7 +352,9 @@ class core_course_external extends external_api {
 
     /**
      * Returns description of method result value
+     *
      * @return external_description
+     * @since Moodle 2.2
      */
     public static function get_courses_returns() {
         return new external_multiple_structure(
@@ -402,7 +420,9 @@ class core_course_external extends external_api {
 
     /**
      * Returns description of method parameters
+     *
      * @return external_function_parameters
+     * @since Moodle 2.2
      */
     public static function create_courses_parameters() {
         $courseconfig = get_config('moodlecourse'); //needed for many default values
@@ -471,8 +491,10 @@ class core_course_external extends external_api {
 
     /**
      * Create  courses
+     *
      * @param array $courses
      * @return array courses (id and shortname only)
+     * @since Moodle 2.2
      */
     public static function create_courses($courses) {
         global $CFG, $DB;
@@ -556,7 +578,9 @@ class core_course_external extends external_api {
 
     /**
      * Returns description of method result value
+     *
      * @return external_description
+     * @since Moodle 2.2
      */
     public static function create_courses_returns() {
         return new external_multiple_structure(
@@ -569,18 +593,82 @@ class core_course_external extends external_api {
         );
     }
 
+    /**
+     * Returns description of method parameters
+     * @return external_function_parameters
+     */
+    public static function delete_courses_parameters() {
+        return new external_function_parameters(
+            array(
+                'courseids' => new external_multiple_structure(new external_value(PARAM_INT, 'course ID')),
+            )
+        );
+    }
+
+    /**
+     * Delete courses
+     * @param array $courseids A list of course ids
+     */
+    public static function delete_courses($courseids) {
+        global $CFG, $DB;
+        require_once($CFG->dirroot."/course/lib.php");
+
+        // Parameter validation.
+        $params = self::validate_parameters(self::delete_courses_parameters(), array('courseids'=>$courseids));
+
+        $transaction = $DB->start_delegated_transaction();
+
+        foreach ($params['courseids'] as $courseid) {
+            $course = $DB->get_record('course', array('id'=>$courseid), '*', MUST_EXIST);
+
+            // Check if the context is valid.
+            $coursecontext = context_course::instance($course->id);
+            self::validate_context($coursecontext);
+
+            // Check if the current user has enought permissions.
+            if (!can_delete_course($courseid)) {
+                throw new moodle_exception('cannotdeletecategorycourse', 'error', '', format_string($course->fullname)." (id: $courseid)");
+            }
+
+            delete_course($course, false);
+        }
+
+        $transaction->allow_commit();
+
+        return null;
+    }
+
+    /**
+     * Returns description of method result value
+     * @return external_description
+     */
+    public static function delete_courses_returns() {
+        return null;
+    }
+
 }
 
 /**
- * Deprecated course functions
- * @deprecated since Moodle 2.2 please use core_course_external instead
+ * Deprecated course external functions
+ *
+ * @package    core_course
+ * @copyright  2009 Petr Skodak
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since Moodle 2.0
+ * @deprecated Moodle 2.2 MDL-29106 - Please do not use this class any more.
+ * @todo MDL-31194 This will be deleted in Moodle 2.5.
+ * @see core_course_external
  */
 class moodle_course_external extends external_api {
 
     /**
      * Returns description of method parameters
-     * @deprecated since Moodle 2.2 please use core_course_external::get_courses_parameters instead
+     *
      * @return external_function_parameters
+     * @since Moodle 2.0
+     * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
+     * @todo MDL-31194 This will be deleted in Moodle 2.5.
+     * @see core_course_external::get_courses_parameters()
      */
     public static function get_courses_parameters() {
         return core_course_external::get_courses_parameters();
@@ -588,9 +676,13 @@ class moodle_course_external extends external_api {
 
     /**
      * Get courses
+     *
      * @param array $options
-     * @deprecated since Moodle 2.2 please use core_course_external::get_courses instead
      * @return array
+     * @since Moodle 2.0
+     * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
+     * @todo MDL-31194 This will be deleted in Moodle 2.5.
+     * @see core_course_external::get_courses()
      */
     public static function get_courses($options) {
         return core_course_external::get_courses($options);
@@ -598,8 +690,12 @@ class moodle_course_external extends external_api {
 
     /**
      * Returns description of method result value
-     * @deprecated since Moodle 2.2 please use core_course_external::get_courses_returns instead
+     *
      * @return external_description
+     * @since Moodle 2.0
+     * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
+     * @todo MDL-31194 This will be deleted in Moodle 2.5.
+     * @see core_course_external::get_courses_returns()
      */
     public static function get_courses_returns() {
         return core_course_external::get_courses_returns();
@@ -607,8 +703,12 @@ class moodle_course_external extends external_api {
 
     /**
      * Returns description of method parameters
-     * @deprecated since Moodle 2.2 please use core_course_external::create_courses_parameters instead
+     *
      * @return external_function_parameters
+     * @since Moodle 2.0
+     * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
+     * @todo MDL-31194 This will be deleted in Moodle 2.5.
+     * @see core_course_external::create_courses_parameters()
      */
     public static function create_courses_parameters() {
         return core_course_external::create_courses_parameters();
@@ -616,9 +716,13 @@ class moodle_course_external extends external_api {
 
     /**
      * Create  courses
-     * @deprecated since Moodle 2.2 please use core_course_external::create_courses instead
+     *
      * @param array $courses
      * @return array courses (id and shortname only)
+     * @since Moodle 2.0
+     * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
+     * @todo MDL-31194 This will be deleted in Moodle 2.5.
+     * @see core_course_external::create_courses()
      */
     public static function create_courses($courses) {
         return core_course_external::create_courses($courses);
@@ -626,8 +730,12 @@ class moodle_course_external extends external_api {
 
     /**
      * Returns description of method result value
-     * @deprecated since Moodle 2.2 please use core_course_external::create_courses_returns instead
+     *
      * @return external_description
+     * @since Moodle 2.0
+     * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
+     * @todo MDL-31194 This will be deleted in Moodle 2.5.
+     * @see core_course_external::create_courses_returns()
      */
     public static function create_courses_returns() {
         return core_course_external::create_courses_returns();
diff --git a/course/format/topics/format.js b/course/format/topics/format.js
new file mode 100644 (file)
index 0000000..26f2801
--- /dev/null
@@ -0,0 +1,40 @@
+// Javascript functions for course format
+
+M.course = M.course || {};
+
+M.course.format = M.course.format || {};
+
+/**
+ * Get section list for this format
+ *
+ * @param {YUI} Y YUI3 instance
+ * @return {string} section list selector
+ */
+M.course.format.get_section_selector = function(Y) {
+    return 'li.section';
+}
+
+/**
+ * Swap section
+ *
+ * @param {YUI} Y YUI3 instance
+ * @param {string} node1 node to swap to
+ * @param {string} node2 node to swap with
+ * @return {NodeList} section list
+ */
+M.course.format.swap_sections = function(Y, node1, node2) {
+    var CSS = {
+        COURSECONTENT : 'course-content',
+        LEFT : 'left',
+        RIGHT : 'right',
+        SECTIONADDMENUS : 'section_add_menus',
+    };
+
+    var sectionlist = Y.Node.all('.'+CSS.COURSECONTENT+' '+M.course.format.get_section_selector(Y));
+    // Swap left block
+    sectionlist.item(node1).one('.'+CSS.LEFT).swap(sectionlist.item(node2).one('.'+CSS.LEFT));
+    // Swap right block
+    sectionlist.item(node1).one('.'+CSS.RIGHT).swap(sectionlist.item(node2).one('.'+CSS.RIGHT));
+    // Swap menus
+    sectionlist.item(node1).one('.'+CSS.SECTIONADDMENUS).swap(sectionlist.item(node2).one('.'+CSS.SECTIONADDMENUS));
+}
index 8772433..fc0d138 100644 (file)
@@ -286,3 +286,6 @@ if (!empty($sectionmenu)) {
     $select->formid = 'sectionmenu';
     echo $OUTPUT->render($select);
 }
+
+    // Include course format js module
+    $PAGE->requires->js('/course/format/topics/format.js');
diff --git a/course/format/weeks/format.js b/course/format/weeks/format.js
new file mode 100644 (file)
index 0000000..d1b5430
--- /dev/null
@@ -0,0 +1,43 @@
+// Javascript functions for course format
+
+M.course = M.course || {};
+
+M.course.format = M.course.format || {};
+
+/**
+ * Get section list for this format
+ *
+ * @param {YUI} Y YUI3 instance
+ * @return {string} section list selector
+ */
+M.course.format.get_section_selector = function(Y) {
+    return 'li.section';
+}
+
+/**
+ * Swap section
+ *
+ * @param {YUI} Y YUI3 instance
+ * @param {string} node1 node to swap to
+ * @param {string} node2 node to swap with
+ * @return {NodeList} section list
+ */
+M.course.format.swap_sections = function(Y, node1, node2) {
+    var CSS = {
+        COURSECONTENT : 'course-content',
+        LEFT : 'left',
+        RIGHT : 'right',
+        SECTIONADDMENUS : 'section_add_menus',
+        WEEKDATES: 'weekdates'
+    };
+
+    var sectionlist = Y.Node.all('.'+CSS.COURSECONTENT+' '+M.course.format.get_section_selector(Y));
+    // Swap left block
+    sectionlist.item(node1).one('.'+CSS.LEFT).swap(sectionlist.item(node2).one('.'+CSS.LEFT));
+    // Swap right block
+    sectionlist.item(node1).one('.'+CSS.RIGHT).swap(sectionlist.item(node2).one('.'+CSS.RIGHT));
+    // Swap menus
+    sectionlist.item(node1).one('.'+CSS.SECTIONADDMENUS).swap(sectionlist.item(node2).one('.'+CSS.SECTIONADDMENUS));
+    // Swap week dates
+    sectionlist.item(node1).one('.'+CSS.WEEKDATES).swap(sectionlist.item(node2).one('.'+CSS.WEEKDATES));
+}
index 36e89fc..ee48a43 100644 (file)
@@ -286,3 +286,6 @@ defined('MOODLE_INTERNAL') || die();
         $select->formid = 'sectionmenu';
         echo $OUTPUT->render($select);
     }
+
+    // Include course format js module
+    $PAGE->requires->js('/course/format/weeks/format.js');
index 382ae35..05e486e 100644 (file)
@@ -3957,6 +3957,32 @@ function create_course($data, $editoroptions = NULL) {
     return $course;
 }
 
+/**
+ * Create a new course category and marks the context as dirty
+ *
+ * This function does not set the sortorder for the new category and
+ * @see{fix_course_sortorder} should be called after creating a new course
+ * category
+ *
+ * Please note that this function does not verify access control.
+ *
+ * @param object $category All of the data required for an entry in the course_categories table
+ * @return object new course category
+ */
+function create_course_category($category) {
+    global $DB;
+
+    $category->timemodified = time();
+    $category->id = $DB->insert_record('course_categories', $category);
+    $category = $DB->get_record('course_categories', array('id' => $category->id));
+
+    // We should mark the context as dirty
+    $category->context = context_coursecat::instance($category->id);
+    $category->context->mark_dirty();
+
+    return $category;
+}
+
 /**
  * Update a course.
  *
@@ -4504,14 +4530,14 @@ function include_course_ajax($course, $modules = array(), $config = null) {
 
     // Include course dragdrop
     if ($course->id != SITEID) {
-        $PAGE->requires->yui_module('moodle-course-dragdrop', 'M.core_course.init_section_dragdrop',
+        $PAGE->requires->yui_module('moodle-course-dragdrop', 'M.course.init_section_dragdrop',
             array(array(
                 'courseid' => $course->id,
                 'ajaxurl' => $config->sectionurl,
                 'config' => $config,
             )), null, true);
 
-        $PAGE->requires->yui_module('moodle-course-dragdrop', 'M.core_course.init_resource_dragdrop',
+        $PAGE->requires->yui_module('moodle-course-dragdrop', 'M.course.init_resource_dragdrop',
             array(array(
                 'courseid' => $course->id,
                 'ajaxurl' => $config->resourceurl,
index 72a2a9e..1fec1fc 100644 (file)
@@ -130,10 +130,6 @@ switch($requestmethod) {
                             $beforemod = NULL;
                         }
 
-                        if (debugging('',DEBUG_DEVELOPER)) {
-                            error_log(serialize($beforemod));
-                        }
-
                         moveto_module($cm, $section, $beforemod);
                         break;
                 }
index cf0822b..f7f4f80 100644 (file)
@@ -96,4 +96,52 @@ class courselib_testcase extends advanced_testcase {
         $CFG->courselistshortnames = 1;
         $this->assertEquals('FROG101 Introduction to pond life', get_course_display_name_for_list($course));
     }
+
+    public function test_create_course_category() {
+        global $CFG, $DB;
+        $this->resetAfterTest(true);
+
+        // Create the category
+        $data = new stdClass();
+        $data->name = 'aaa';
+        $data->description = 'aaa';
+        $data->idnumber = '';
+
+        $category1 = create_course_category($data);
+
+        // Initially confirm that base data was inserted correctly
+        $this->assertEquals($data->name, $category1->name);
+        $this->assertEquals($data->description, $category1->description);
+        $this->assertEquals($data->idnumber, $category1->idnumber);
+
+        // sortorder should be blank initially
+        $this->assertEmpty($category1->sortorder);
+
+        // Calling fix_course_sortorder() should provide a new sortorder
+        fix_course_sortorder();
+        $category1 = $DB->get_record('course_categories', array('id' => $category1->id));
+
+        $this->assertGreaterThanOrEqual(1, $category1->sortorder);
+
+        // Create two more categories and test the sortorder worked correctly
+        $data->name = 'ccc';
+        $category2 = create_course_category($data);
+        $this->assertEmpty($category2->sortorder);
+
+        $data->name = 'bbb';
+        $category3 = create_course_category($data);
+        $this->assertEmpty($category3->sortorder);
+
+        // Calling fix_course_sortorder() should provide a new sortorder to give category1,
+        // category2, category3. New course categories are ordered by id not name
+        fix_course_sortorder();
+
+        $category1 = $DB->get_record('course_categories', array('id' => $category1->id));
+        $category2 = $DB->get_record('course_categories', array('id' => $category2->id));
+        $category3 = $DB->get_record('course_categories', array('id' => $category3->id));
+
+        $this->assertGreaterThanOrEqual($category1->sortorder, $category2->sortorder);
+        $this->assertGreaterThanOrEqual($category2->sortorder, $category3->sortorder);
+        $this->assertGreaterThanOrEqual($category1->sortorder, $category3->sortorder);
+    }
 }
index f24dbf6..595252d 100644 (file)
@@ -25,6 +25,8 @@ YUI.add('moodle-course-dragdrop', function(Y) {
         DRAGSECTION.superclass.constructor.apply(this, arguments);
     };
     Y.extend(DRAGSECTION, M.core.dragdrop, {
+        sectionlistselector : null,
+
         initializer : function(params) {
             // Set group for parent class
             this.groups = ['section'];
@@ -36,8 +38,10 @@ YUI.add('moodle-course-dragdrop', function(Y) {
                 return false;
             }
             // Initialise sections dragging
-            this.setup_for_section('.'+CSS.COURSECONTENT+' li.'+CSS.SECTION);
-            M.course.coursebase.register_module(this);
+            if (M.course.format && M.course.format.get_section_selector && typeof(M.course.format.get_section_selector) == 'function') {
+                this.sectionlistselector = '.'+CSS.COURSECONTENT+' '+M.course.format.get_section_selector(Y);
+                this.setup_for_section(this.sectionlistselector);
+            }
         },
 
          /**
@@ -131,7 +135,7 @@ YUI.add('moodle-course-dragdrop', function(Y) {
 
             // Get the list of nodes
             drag.get('dragNode').removeClass(CSS.COURSECONTENT);
-            var sectionlist = Y.Node.all('.'+CSS.COURSECONTENT+' li.'+CSS.SECTION);
+            var sectionlist = Y.Node.all(this.sectionlistselector);
 
             // Add lightbox if it not there
             var lightbox = M.util.add_lightbox(Y, dragnode);
@@ -176,16 +180,9 @@ YUI.add('moodle-course-dragdrop', function(Y) {
                                     var sectionid = sectionlist.item(i-1).get('id');
                                     sectionlist.item(i-1).set('id', sectionlist.item(i).get('id'));
                                     sectionlist.item(i).set('id', sectionid);
-                                    // Swap left block
-                                    sectionlist.item(i-1).one('.'+CSS.LEFT).swap(sectionlist.item(i).one('.'+CSS.LEFT));
-                                    // Swap right block
-                                    sectionlist.item(i-1).one('.'+CSS.RIGHT).swap(sectionlist.item(i).one('.'+CSS.RIGHT));
-                                    // Swap menus
-                                    sectionlist.item(i-1).one('.'+CSS.SECTIONADDMENUS).swap(sectionlist.item(i).one('.'+CSS.SECTIONADDMENUS));
-                                    // Swap week dates if in weekly format
-                                    var weekdates = sectionlist.item(i-1).one('.'+CSS.WEEKDATES);
-                                    if (weekdates) {
-                                        weekdates.swap(sectionlist.item(i).one('.'+CSS.WEEKDATES));
+                                    // See what format needs to be swapped
+                                    if (M.course.format && M.course.format.swap_sections && typeof(M.course.format.swap_sections) == 'function') {
+                                        M.course.format.swap_sections(Y, i-1, i);
                                     }
                                     // Update flag
                                     swapped = true;
@@ -229,9 +226,12 @@ YUI.add('moodle-course-dragdrop', function(Y) {
             this.parentnodeclass = CSS.SECTION;
 
             // Go through all sections
-            this.setup_for_section('.'+CSS.COURSECONTENT+' li.'+CSS.SECTION);
-            M.course.coursebase.register_module(this);
-            M.course.dragres = this;
+            if (M.course.format && M.course.format.get_section_selector && typeof(M.course.format.get_section_selector) == 'function') {
+                var sectionlistselector = '.'+CSS.COURSECONTENT+' '+M.course.format.get_section_selector(Y);
+                this.setup_for_section(sectionlistselector);
+                M.course.coursebase.register_module(this);
+                M.course.dragres = this;
+            }
         },
 
          /**
@@ -311,6 +311,8 @@ YUI.add('moodle-course-dragdrop', function(Y) {
             var dragnode = drag.get('node');
             var dropnode = e.drop.get('node');
 
+            var sectionselector = M.course.format.get_section_selector(Y);
+
             var params = {};
 
             // Handle any variables which we must pass back through to
@@ -325,7 +327,7 @@ YUI.add('moodle-course-dragdrop', function(Y) {
             params['class'] = 'resource';
             params.field = 'move';
             params.id = Number(this.get_resource_id(dragnode));
-            params.sectionId = this.get_section_id(dropnode.ancestor('li.'+CSS.SECTION));
+            params.sectionId = this.get_section_id(dropnode.ancestor(sectionselector));
 
             if (dragnode.next()) {
                 params.beforeId = Number(this.get_resource_id(dragnode.next()));
@@ -368,11 +370,11 @@ YUI.add('moodle-course-dragdrop', function(Y) {
         }
     });
 
-    M.core_course = M.core_course || {};
-    M.core_course.init_resource_dragdrop = function(params) {
+    M.course = M.course || {};
+    M.course.init_resource_dragdrop = function(params) {
         new DRAGRESOURCE(params);
     }
-    M.core_course.init_section_dragdrop = function(params) {
+    M.course.init_section_dragdrop = function(params) {
         new DRAGSECTION(params);
     }
 }, '@VERSION@', {requires:['base', 'node', 'io', 'dom', 'dd', 'moodle-core-dragdrop', 'moodle-enrol-notification', 'moodle-course-coursebase']});
diff --git a/enrol/authorize/db/messages.php b/enrol/authorize/db/messages.php
new file mode 100644 (file)
index 0000000..8d3df01
--- /dev/null
@@ -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/>.
+
+/**
+ * Defines message providers (types of message sent) for the PayPal enrolment plugin.
+ *
+ * @package    enrol_authorize
+ * @copyright  2012 Andrew Davis
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+$messageproviders = array(
+    'authorize_enrolment' => array(),
+);
index 272fc19..2a7d367 100644 (file)
@@ -26,7 +26,7 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$plugin->version   = 2011112900;        // The current plugin version (Date: YYYYMMDDXX)
+$plugin->version   = 2011112901;        // The current plugin version (Date: YYYYMMDDXX)
 $plugin->requires  = 2011112900;        // Requires this Moodle version
 $plugin->component = 'enrol_authorize'; // Full name of the plugin (used for diagnostics)
 $plugin->cron      = 180;
index 71e0f69..57b6ac1 100644 (file)
@@ -1,5 +1,4 @@
 <?php
-
 // This file is part of Moodle - http://moodle.org/
 //
 // Moodle is free software: you can redistribute it and/or modify
 // You should have received a copy of the GNU General Public License
 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
+
 /**
  * External course participation api.
  *
  * This api is mostly read only, the actual enrol and unenrol
  * support is in each enrol plugin.
  *
- * @package    core
- * @subpackage enrol
- * @copyright  2009 Moodle Pty Ltd (http://moodle.com)
+ * @package    core_enrol
+ * @category   external
+ * @copyright  2010 Jerome Mouneyrac
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
@@ -32,12 +32,19 @@ defined('MOODLE_INTERNAL') || die();
 require_once("$CFG->libdir/externallib.php");
 
 /**
- * Enrol functions
+ * Enrol external functions
+ *
+ * @package    core_enrol
+ * @category   external
+ * @copyright  2011 Jerome Mouneyrac
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since Moodle 2.2
  */
 class core_enrol_external extends external_api {
 
     /**
      * Returns description of method parameters
+     *
      * @return external_function_parameters
      */
     public static function get_users_courses_parameters() {
@@ -50,7 +57,6 @@ class core_enrol_external extends external_api {
 
     /**
      * Get list of courses user is enrolled in (only active enrolments are returned).
-     *
      * Please note the current user must be able to access the course, otherwise the course is not included.
      *
      * @param int $userid
@@ -92,6 +98,7 @@ class core_enrol_external extends external_api {
 
     /**
      * Returns description of method result value
+     *
      * @return external_description
      */
     public static function get_users_courses_returns() {
@@ -111,6 +118,7 @@ class core_enrol_external extends external_api {
 
     /**
      * Returns description of method parameters
+     *
      * @return external_function_parameters
      */
     public static function get_enrolled_users_parameters() {
@@ -136,11 +144,12 @@ class core_enrol_external extends external_api {
 
     /**
      * Get course participants details
+     *
      * @param int $courseid  course id
      * @param array $options options {
-     *          'name' => option name
-     *          'value' => option value
-     * }
+     *                                'name' => option name
+     *                                'value' => option value
+     *                               }
      * @return array An array of users
      */
     public static function get_enrolled_users($courseid, $options) {
@@ -241,6 +250,7 @@ class core_enrol_external extends external_api {
 
     /**
      * Returns description of method result value
+     *
      * @return external_description
      */
     public static function get_enrolled_users_returns() {
@@ -322,12 +332,19 @@ class core_enrol_external extends external_api {
 }
 
 /**
- * Role functions
+ * Role external functions
+ *
+ * @package    core_role
+ * @category   external
+ * @copyright  2011 Jerome Mouneyrac
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since Moodle 2.2
  */
 class core_role_external extends external_api {
 
     /**
      * Returns description of method parameters
+     *
      * @return external_function_parameters
      */
     public static function assign_roles_parameters() {
@@ -349,8 +366,7 @@ class core_role_external extends external_api {
     /**
      * Manual role assignments to users
      *
-     * @param array $assignment  An array of manual role assignment
-     * @return null
+     * @param array $assignments An array of manual role assignment
      */
     public static function assign_roles($assignments) {
         global $DB;
@@ -382,7 +398,8 @@ class core_role_external extends external_api {
 
     /**
      * Returns description of method result value
-     * @return external_description
+     *
+     * @return null
      */
     public static function assign_roles_returns() {
         return null;
@@ -391,6 +408,7 @@ class core_role_external extends external_api {
 
     /**
      * Returns description of method parameters
+     *
      * @return external_function_parameters
      */
     public static function unassign_roles_parameters() {
@@ -412,8 +430,7 @@ class core_role_external extends external_api {
      /**
      * Unassign roles from users
      *
-     * @param array $unassignment  An array of unassignment
-     * @return null
+     * @param array $unassignments An array of unassignment
      */
     public static function unassign_roles($unassignments) {
          global $DB;
@@ -444,6 +461,7 @@ class core_role_external extends external_api {
 
    /**
      * Returns description of method result value
+     *
      * @return null
      */
     public static function unassign_roles_returns() {
@@ -453,16 +471,28 @@ class core_role_external extends external_api {
 
 
 /**
- * Deprecated enroll and role functions
- * @deprecated since Moodle 2.2 please use core_enrol_external or core_role_external instead
+ * Deprecated enrol and role external functions
+ *
+ * @package    core_enrol
+ * @copyright  2010 Jerome Mouneyrac
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since Moodle 2.0
+ * @deprecated Moodle 2.2 MDL-29106 - Please do not use this class any more.
+ * @todo MDL-31194 This will be deleted in Moodle 2.5.
+ * @see core_enrol_external
+ * @see core_role_external
  */
 class moodle_enrol_external extends external_api {
 
 
     /**
      * Returns description of method parameters
-     * @deprecated since Moodle 2.2 please use core_enrol_external::get_enrolled_users_parameters() instead
+     *
      * @return external_function_parameters
+     * @since Moodle 2.0
+     * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
+     * @todo MDL-31194 This will be deleted in Moodle 2.5.
+     * @see core_enrol_external::get_enrolled_users_parameters()
      */
     public static function get_enrolled_users_parameters() {
         return new external_function_parameters(
@@ -477,12 +507,16 @@ class moodle_enrol_external extends external_api {
 
     /**
      * Get list of course participants.
-     * @deprecated since Moodle 2.2 please use core_enrol_external::get_enrolled_users() instead
+     *
      * @param int $courseid
      * @param text $withcapability
      * @param int $groupid
      * @param bool $onlyactive
      * @return array of course participants
+     * @since Moodle 2.0
+     * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
+     * @todo MDL-31194 This will be deleted in Moodle 2.5.
+     * @see core_enrol_external::get_enrolled_users()
      */
     public static function get_enrolled_users($courseid, $withcapability = null, $groupid = null, $onlyactive = false) {
         global $DB, $CFG, $USER;
@@ -568,8 +602,12 @@ class moodle_enrol_external extends external_api {
 
     /**
      * Returns description of method result value
-     * @deprecated since Moodle 2.2 please use core_enrol_external::get_enrolled_users_returns() instead
+     *
      * @return external_description
+     * @since Moodle 2.0
+     * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
+     * @todo MDL-31194 This will be deleted in Moodle 2.5.
+     * @see core_enrol_external::get_enrolled_users_returns()
      */
     public static function get_enrolled_users_returns() {
         return new external_multiple_structure(
@@ -590,8 +628,12 @@ class moodle_enrol_external extends external_api {
 
     /**
      * Returns description of method parameters
-     * @deprecated since Moodle 2.2 please use core_enrol_external::get_users_courses_parameters() instead
+     *
      * @return external_function_parameters
+     * @since Moodle 2.1
+     * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
+     * @todo MDL-31194 This will be deleted in Moodle 2.5.
+     * @see core_enrol_external::get_users_courses_parameters()
      */
     public static function get_users_courses_parameters() {
         return core_enrol_external::get_users_courses_parameters();
@@ -599,11 +641,14 @@ class moodle_enrol_external extends external_api {
 
     /**
      * Get list of courses user is enrolled in (only active enrolments are returned).
-     *
      * Please note the current user must be able to access the course, otherwise the course is not included.
-     * @deprecated since Moodle 2.2 please use core_enrol_external::get_users_courses() instead
+     *
      * @param int $userid
      * @return array of courses
+     * @since Moodle 2.1
+     * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
+     * @todo MDL-31194 This will be deleted in Moodle 2.5.
+     * @see use core_enrol_external::get_users_courses()
      */
     public static function get_users_courses($userid) {
         return core_enrol_external::get_users_courses($userid);
@@ -611,8 +656,12 @@ class moodle_enrol_external extends external_api {
 
     /**
      * Returns description of method result value
-     * @deprecated since Moodle 2.2 please use core_enrol_external::get_users_courses_returns() instead
+     *
      * @return external_description
+     * @since Moodle 2.1
+     * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
+     * @todo MDL-31194 This will be deleted in Moodle 2.5.
+     * @see core_enrol_external::get_users_courses_returns()
      */
     public static function get_users_courses_returns() {
         return core_enrol_external::get_users_courses_returns();
@@ -621,8 +670,12 @@ class moodle_enrol_external extends external_api {
 
     /**
      * Returns description of method parameters
-     * @deprecated since Moodle 2.2 please use core_role_external::assign_roles_parameters() instead
+     *
      * @return external_function_parameters
+     * @since Moodle 2.0
+     * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
+     * @todo MDL-31194 This will be deleted in Moodle 2.5.
+     * @see core_role_external::assign_roles_parameters()
      */
     public static function role_assign_parameters() {
         return core_role_external::assign_roles_parameters();
@@ -630,9 +683,12 @@ class moodle_enrol_external extends external_api {
 
     /**
      * Manual role assignments to users
-     * @deprecated since Moodle 2.2 please use core_role_external::assign_roles() instead
-     * @param array $assignment  An array of manual role assignment
-     * @return null
+     *
+     * @param array $assignments An array of manual role assignment
+     * @since Moodle 2.0
+     * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
+     * @todo MDL-31194 This will be deleted in Moodle 2.5.
+     * @see core_role_external::assign_roles()
      */
     public static function role_assign($assignments) {
         return core_role_external::assign_roles($assignments);
@@ -640,8 +696,12 @@ class moodle_enrol_external extends external_api {
 
     /**
      * Returns description of method result value
-     * @deprecated since Moodle 2.2 please use core_role_external::assign_roles_returns() instead
-     * @return external_description
+     *
+     * @return null
+     * @since Moodle 2.0
+     * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
+     * @todo MDL-31194 This will be deleted in Moodle 2.5.
+     * @see core_role_external::assign_roles_returns()
      */
     public static function role_assign_returns() {
         return core_role_external::assign_roles_returns();
@@ -650,8 +710,12 @@ class moodle_enrol_external extends external_api {
 
     /**
      * Returns description of method parameters
-     * @deprecated since Moodle 2.2 please use core_role_external::unassign_roles_parameters() instead
+     *
      * @return external_function_parameters
+     * @since Moodle 2.0
+     * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
+     * @todo MDL-31194 This will be deleted in Moodle 2.5.
+     * @see core_role_external::unassign_roles_parameters()
      */
     public static function role_unassign_parameters() {
         return core_role_external::unassign_roles_parameters();
@@ -659,9 +723,12 @@ class moodle_enrol_external extends external_api {
 
      /**
      * Unassign roles from users
-     * @deprecated since Moodle 2.2 please use core_role_external::unassign_roles() instead
-     * @param array $unassignment  An array of unassignment
-     * @return null
+     *
+     * @param array $unassignments An array of unassignment
+     * @since Moodle 2.0
+     * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
+     * @todo MDL-31194 This will be deleted in Moodle 2.5.
+     * @see core_role_external::unassign_roles()
      */
     public static function role_unassign($unassignments) {
          return core_role_external::unassign_roles($unassignments);
@@ -669,8 +736,12 @@ class moodle_enrol_external extends external_api {
 
    /**
      * Returns description of method result value
-    * @deprecated since Moodle 2.2 please use core_role_external::unassign_roles_returns() instead
-     * @return external_description
+     *
+     * @return null
+     * @since Moodle 2.0
+     * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
+     * @todo MDL-31194 This will be deleted in Moodle 2.5.
+     * @see core_role_external::unassign_roles_returns()
      */
     public static function role_unassign_returns() {
         return core_role_external::unassign_roles_returns();
diff --git a/enrol/flatfile/db/messages.php b/enrol/flatfile/db/messages.php
new file mode 100644 (file)
index 0000000..6df3459
--- /dev/null
@@ -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/>.
+
+/**
+ * Defines message providers (types of message sent) for the PayPal enrolment plugin.
+ *
+ * @package    enrol_flatfile
+ * @copyright  2012 Andrew Davis
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+$messageproviders = array(
+    'flatfile_enrolment' => array(),
+);
index 9dee198..9d992cf 100644 (file)
@@ -30,6 +30,7 @@ $string['location'] = 'File location';
 $string['mailadmin'] = 'Notify admin by email';
 $string['mailstudents'] = 'Notify students by email';
 $string['mailteachers'] = 'Notify teachers by email';
+$string['messageprovider:flatfile_enrolment'] = 'Flat file enrolment messages';
 $string['mapping'] = 'Flat file mapping';
 $string['pluginname'] = 'Flat file (CSV)';
 $string['pluginname_desc'] = 'This method will repeatedly check for and process a specially-formatted text file in the location that you specify.
index c6a5a5a..b0acb3f 100644 (file)
@@ -26,7 +26,7 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$plugin->version   = 2011112900;        // The current plugin version (Date: YYYYMMDDXX)
+$plugin->version   = 2011112901;        // The current plugin version (Date: YYYYMMDDXX)
 $plugin->requires  = 2011112900;        // Requires this Moodle version
 $plugin->component = 'enrol_flatfile';  // Full name of the plugin (used for diagnostics)
 $plugin->cron      = 60;
diff --git a/enrol/imsenterprise/db/messages.php b/enrol/imsenterprise/db/messages.php
new file mode 100644 (file)
index 0000000..d75d424
--- /dev/null
@@ -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/>.
+
+/**
+ * Defines message providers (types of message sent) for the PayPal enrolment plugin.
+ *
+ * @package    enrol_imsenterprise
+ * @copyright  2012 Andrew Davis
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+$messageproviders = array(
+    'imsenterprise_enrolment' => array(),
+);
index 9ceb277..8b91682 100644 (file)
@@ -54,6 +54,7 @@ $string['location'] = 'File location';
 $string['logtolocation'] = 'Log file output location (blank for no logging)';
 $string['mailadmins'] = 'Notify admin by email';
 $string['mailusers'] = 'Notify users by email';
+$string['messageprovider:imsenterprise_enrolment'] = 'IMS Enterprise enrolment messages';
 $string['miscsettings'] = 'Miscellaneous';
 $string['processphoto'] = 'Add user photo data to profile';
 $string['processphotowarning'] = 'Warning: Image processing is likely to add a significant burden to the server. You are recommended not to activate this option if large numbers of students are expected to be processed.';
index 0d18ddb..4397178 100644 (file)
@@ -25,7 +25,7 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$plugin->version   = 2011112900;        // The current plugin version (Date: YYYYMMDDXX)
+$plugin->version   = 2011112901;        // The current plugin version (Date: YYYYMMDDXX)
 $plugin->requires  = 2011112900;        // Requires this Moodle version
 $plugin->component = 'enrol_imsenterprise'; // Full name of the plugin (used for diagnostics)
 $plugin->cron      = 60;
index f2daf62..44d90ac 100644 (file)
 // You should have received a copy of the GNU General Public License
 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
+
 /**
  * External course participation api.
  *
  * This api is mostly read only, the actual enrol and unenrol
  * support is in each enrol plugin.
  *
- * @package    enrol
- * @subpackage manual
- * @copyright  2011 Moodle Pty Ltd (http://moodle.com)
+ * @package    enrol_manual
+ * @category   external
+ * @copyright  2011 Jerome Mouneyrac
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
+
 defined('MOODLE_INTERNAL') || die();
 
 require_once("$CFG->libdir/externallib.php");
 
 /**
- * Manual enrolment functions
+ * Manual enrolment external functions
+ *
+ * @package    enrol_manual
+ * @category   external
+ * @copyright  2011 Jerome Mouneyrac
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since Moodle 2.2
  */
 class enrol_manual_external extends external_api {
 
     /**
      * Returns description of method parameters
+     *
      * @return external_function_parameters
+     * @since Moodle 2.2
      */
     public static function enrol_users_parameters() {
         return new external_function_parameters(
@@ -59,9 +69,10 @@ class enrol_manual_external extends external_api {
 
     /**
      * Enrolment of users
+     *
      * Function throw an exception at the first error encountered.
      * @param array $enrolments  An array of user enrolment
-     * @return null
+     * @since Moodle 2.2
      */
     public static function enrol_users($enrolments) {
         global $DB, $CFG;
@@ -137,7 +148,9 @@ class enrol_manual_external extends external_api {
 
     /**
      * Returns description of method result value
+     *
      * @return null
+     * @since Moodle 2.2
      */
     public static function enrol_users_returns() {
         return null;
@@ -146,15 +159,26 @@ class enrol_manual_external extends external_api {
 }
 
 /**
- * Deprecated manual enrolment functions
- * @deprecated since Moodle 2.2 please use enrol_manual_external instead
+ * Deprecated manual enrolment external functions
+ *
+ * @package    enrol_manual
+ * @copyright  2011 Jerome Mouneyrac
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since Moodle 2.0
+ * @deprecated Moodle 2.2 MDL-29106 - Please do not use this class any more.
+ * @todo MDL-31194 This will be deleted in Moodle 2.5.
+ * @see enrol_manual_external
  */
 class moodle_enrol_manual_external extends external_api {
 
     /**
      * Returns description of method parameters
-     * @deprecated since Moodle 2.2 please use enrol_manual_external::enrol_users_parameters instead
+     *
      * @return external_function_parameters
+     * @since Moodle 2.0
+     * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
+     * @todo MDL-31194 This will be deleted in Moodle 2.5.
+     * @see enrol_manual_external::enrol_users_parameters()
      */
     public static function manual_enrol_users_parameters() {
         return enrol_manual_external::enrol_users_parameters();
@@ -163,9 +187,12 @@ class moodle_enrol_manual_external extends external_api {
     /**
      * Enrolment of users
      * Function throw an exception at the first error encountered.
-     * @deprecated since Moodle 2.2 please use enrol_manual_external::enrol_users instead
+     *
      * @param array $enrolments  An array of user enrolment
-     * @return null
+     * @since Moodle 2.0
+     * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
+     * @todo MDL-31194 This will be deleted in Moodle 2.5.
+     * @see enrol_manual_external::enrol_users()
      */
     public static function manual_enrol_users($enrolments) {
         return enrol_manual_external::enrol_users($enrolments);
@@ -173,8 +200,12 @@ class moodle_enrol_manual_external extends external_api {
 
     /**
      * Returns description of method result value
-     * @deprecated since Moodle 2.2 please use enrol_manual_external::enrol_users_returns instead
-     * @return external_description
+     *
+     * @return nul
+     * @since Moodle 2.0
+     * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
+     * @todo MDL-31194 This will be deleted in Moodle 2.5.
+     * @see enrol_manual_external::enrol_users_returns()
      */
     public static function manual_enrol_users_returns() {
         return enrol_manual_external::enrol_users_returns();
diff --git a/enrol/paypal/db/messages.php b/enrol/paypal/db/messages.php
new file mode 100644 (file)
index 0000000..c079e87
--- /dev/null
@@ -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/>.
+
+/**
+ * Defines message providers (types of message sent) for the PayPal enrolment plugin.
+ *
+ * @package    enrol_paypal
+ * @copyright  2012 Chris Follin
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+$messageproviders = array(
+    'paypal_enrolment' => array(),
+);
index 211b9a8..d15a2bb 100644 (file)
@@ -44,6 +44,7 @@ $string['enrolstartdate_help'] = 'If enabled, users can be enrolled from this da
 $string['mailadmins'] = 'Notify admin';
 $string['mailstudents'] = 'Notify students';
 $string['mailteachers'] = 'Notify teachers';
+$string['messageprovider:paypal_enrolment'] = 'PayPal enrolment messages';
 $string['nocost'] = 'There is no cost associated with enrolling in this course!';
 $string['paypal:config'] = 'Configure PayPal enrol instances';
 $string['paypal:manage'] = 'Manage enrolled users';
index 461e7c1..dbe9a61 100644 (file)
@@ -26,6 +26,6 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$plugin->version   = 2011112900;        // The current plugin version (Date: YYYYMMDDXX)
+$plugin->version   = 2011112901;        // The current plugin version (Date: YYYYMMDDXX)
 $plugin->requires  = 2011112900;        // Requires this Moodle version
 $plugin->component = 'enrol_paypal';    // Full name of the plugin (used for diagnostics)
index 530f18d..ec16724 100644 (file)
@@ -1,5 +1,4 @@
 <?php
-
 // This file is part of Moodle - http://moodle.org/
 //
 // Moodle is free software: you can redistribute it and/or modify
 // You should have received a copy of the GNU General Public License
 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
+
 /**
  * External files API
  *
- * @package    moodlecore
- * @subpackage webservice
- * @copyright  2010 Dongsheng Cai <dongsheng@moodle.com>
+ * @package    core_files
+ * @category   external
+ * @copyright  2010 Dongsheng Cai
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
@@ -28,13 +28,21 @@ require_once("$CFG->libdir/externallib.php");
 require_once("$CFG->libdir/filelib.php");
 
 /**
- * Files functions
+ * Files external functions
+ *
+ * @package    core_files
+ * @category   external
+ * @copyright  2011 Jerome Mouneyrac
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since Moodle 2.2
  */
 class core_files_external extends external_api {
 
     /**
      * Returns description of get_files parameters
+     *
      * @return external_function_parameters
+     * @since Moodle 2.2
      */
     public static function get_files_parameters() {
         return new external_function_parameters(
@@ -51,13 +59,15 @@ class core_files_external extends external_api {
 
     /**
      * Return moodle files listing
-     * @param int $contextid
-     * @param int $component
-     * @param int $filearea
-     * @param int $itemid
-     * @param string $filepath
-     * @param string $filename
+     *
+     * @param int $contextid context id
+     * @param int $component component
+     * @param int $filearea file aera
+     * @param int $itemid item id
+     * @param string $filepath file path
+     * @param string $filename file name
      * @return array
+     * @since Moodle 2.2
      */
     public static function get_files($contextid, $component, $filearea, $itemid, $filepath, $filename) {
         global $CFG, $USER, $OUTPUT;
@@ -136,7 +146,9 @@ class core_files_external extends external_api {
 
     /**
      * Returns description of get_files returns
-     * @return external_multiple_structure
+     *
+     * @return external_single_structure
+     * @since Moodle 2.2
      */
     public static function get_files_returns() {
         return new external_single_structure(
@@ -173,7 +185,9 @@ class core_files_external extends external_api {
 
     /**
      * Returns description of upload parameters
+     *
      * @return external_function_parameters
+     * @since Moodle 2.2
      */
     public static function upload_parameters() {
         return new external_function_parameters(
@@ -192,14 +206,15 @@ class core_files_external extends external_api {
     /**
      * Uploading a file to moodle
      *
-     * @param int $contextid
-     * @param string $component
-     * @param string $filearea
-     * @param int $itemid
-     * @param string $filepath
-     * @param string $filename
-     * @param string $filecontent
+     * @param int $contextid context id
+     * @param string $component component
+     * @param string $filearea file aera
+     * @param int $itemid item id
+     * @param string $filepath file path
+     * @param string $filename file name
+     * @param string $filecontent file content
      * @return array
+     * @since Moodle 2.2
      */
     public static function upload($contextid, $component, $filearea, $itemid, $filepath, $filename, $filecontent) {
         global $USER, $CFG;
@@ -235,7 +250,7 @@ class core_files_external extends external_api {
         }
 
         if (isset($fileinfo['itemid'])) {
-            // TODO: in user private area, itemid is always 0
+            // TODO MDL-31116 in user private area, itemid is always 0
             $itemid = 0;
         } else {
             throw new coding_exception('itemid cannot be empty');
@@ -250,7 +265,7 @@ class core_files_external extends external_api {
         if (!($fileinfo['component'] == 'user' and $fileinfo['filearea'] == 'private')) {
             throw new coding_exception('File can be uploaded to user private area only');
         } else {
-            // TODO: hard-coded to use user_private area
+            // TODO MDL-31116 hard-coded to use user_private area
             $component = 'user';
             $filearea = 'private';
         }
@@ -283,7 +298,9 @@ class core_files_external extends external_api {
 
     /**
      * Returns description of upload returns
-     * @return external_multiple_structure
+     *
+     * @return external_single_structure
+     * @since Moodle 2.2
      */
     public static function upload_returns() {
         return new external_single_structure(
@@ -301,15 +318,26 @@ class core_files_external extends external_api {
 }
 
 /**
- * Deprecated files functions
- * @deprecated since Moodle 2.2 please use core_files_external instead
+ * Deprecated files external functions
+ *
+ * @package    core_files
+ * @copyright  2010 Dongsheng Cai
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since Moodle 2.0
+ * @deprecated Moodle 2.2 MDL-29106 - Please do not use this class any more.
+ * @todo MDL-31194 This will be deleted in Moodle 2.5.
+ * @see core_files_external
  */
 class moodle_file_external extends external_api {
 
     /**
      * Returns description of get_files parameters
-     * @deprecated since Moodle 2.2 please use core_files_external::get_files_parameters instead
+     *
      * @return external_function_parameters
+     * @since Moodle 2.0
+     * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
+     * @todo MDL-31194 This will be deleted in Moodle 2.5.
+     * @see core_files_external::get_files_parameters()
      */
     public static function get_files_parameters() {
         return core_files_external::get_files_parameters();
@@ -317,7 +345,7 @@ class moodle_file_external extends external_api {
 
     /**
      * Return moodle files listing
-     * @deprecated since Moodle 2.2 please use core_files_external::get_files instead
+     *
      * @param int $contextid
      * @param int $component
      * @param int $filearea
@@ -325,6 +353,10 @@ class moodle_file_external extends external_api {
      * @param string $filepath
      * @param string $filename
      * @return array
+     * @since Moodle 2.0
+     * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
+     * @todo MDL-31194 This will be deleted in Moodle 2.5.
+     * @see core_files_external::get_files()
      */
     public static function get_files($contextid, $component, $filearea, $itemid, $filepath, $filename) {
         return core_files_external::get_files($contextid, $component, $filearea, $itemid, $filepath, $filename);
@@ -332,8 +364,12 @@ class moodle_file_external extends external_api {
 
     /**
      * Returns description of get_files returns
-     * @deprecated since Moodle 2.2 please use core_files_external::get_files_returns instead
-     * @return external_multiple_structure
+     *
+     * @return external_single_structure
+     * @since Moodle 2.0
+     * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
+     * @todo MDL-31194 This will be deleted in Moodle 2.5.
+     * @see core_files_external::get_files_returns()
      */
     public static function get_files_returns() {
         return core_files_external::get_files_returns();
@@ -341,8 +377,12 @@ class moodle_file_external extends external_api {
 
     /**
      * Returns description of upload parameters
-     * @deprecated since Moodle 2.2 please use core_files_external::upload_parameters instead
+     *
      * @return external_function_parameters
+     * @since Moodle 2.0
+     * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
+     * @todo MDL-31194 This will be deleted in Moodle 2.5.
+     * @see core_files_external::upload_parameters()
      */
     public static function upload_parameters() {
         return core_files_external::upload_parameters();
@@ -350,7 +390,7 @@ class moodle_file_external extends external_api {
 
     /**
      * Uploading a file to moodle
-     * @deprecated since Moodle 2.2 please use core_files_external::upload instead
+     *
      * @param int $contextid
      * @param string $component
      * @param string $filearea
@@ -359,6 +399,10 @@ class moodle_file_external extends external_api {
      * @param string $filename
      * @param string $filecontent
      * @return array
+     * @since Moodle 2.0
+     * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
+     * @todo MDL-31194 This will be deleted in Moodle 2.5.
+     * @see core_files_external::upload()
      */
     public static function upload($contextid, $component, $filearea, $itemid, $filepath, $filename, $filecontent) {
         return core_files_external::upload($contextid, $component, $filearea, $itemid, $filepath, $filename, $filecontent);
@@ -366,8 +410,12 @@ class moodle_file_external extends external_api {
 
     /**
      * Returns description of upload returns
-     * @deprecated since Moodle 2.2 please use core_files_external::upload_returns instead
-     * @return external_multiple_structure
+     *
+     * @return external_single_structure
+     * @since Moodle 2.0
+     * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
+     * @todo MDL-31194 This will be deleted in Moodle 2.5.
+     * @see core_files_external::upload_returns()
      */
     public static function upload_returns() {
         return core_files_external::upload_returns();
index a184226..28355e2 100644 (file)
@@ -80,7 +80,7 @@ class edit_grade_form extends moodleform {
             }
 
             $mform->addElement('select', 'finalgrade', get_string('finalgrade', 'grades'), $scaleopt);
-            $mform->addHelpButton('finalgrade', 'finalgrade', 'finalgrade');
+            $mform->addHelpButton('finalgrade', 'finalgrade', 'grades');
             $mform->disabledIf('finalgrade', 'overridden', 'notchecked');
         }
 
diff --git a/grade/grading/form/guide/README b/grade/grading/form/guide/README
new file mode 100644 (file)
index 0000000..ddbd871
--- /dev/null
@@ -0,0 +1,4 @@
+Marking Guide grading form written by Dan Marsden <dan@danmarsden.com>
+
+based on Lightwork Rubric type 2 format and the spec available here:
+http://docs.moodle.org/dev/Lightwork
diff --git a/grade/grading/form/guide/backup/moodle2/backup_gradingform_guide_plugin.class.php b/grade/grading/form/guide/backup/moodle2/backup_gradingform_guide_plugin.class.php
new file mode 100644 (file)
index 0000000..75af83a
--- /dev/null
@@ -0,0 +1,122 @@
+<?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/>.
+
+/**
+ * Support for backup API
+ *
+ * @package    gradingform_guide
+ * @copyright  2012 Dan Marsden <dan@danmarsden.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Defines marking guide backup structures
+ *
+ * @package    gradingform_guide
+ * @copyright  2012 Dan Marsden <dan@danmarsden.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class backup_gradingform_guide_plugin extends backup_gradingform_plugin {
+
+    /**
+     * Declares marking guide structures to append to the grading form definition
+     * @return backup_plugin_element
+     */
+    protected function define_definition_plugin_structure() {
+
+        // Append data only if the grand-parent element has 'method' set to 'guide'.
+        $plugin = $this->get_plugin_element(null, '../../method', 'guide');
+
+        // Create a visible container for our data.
+        $pluginwrapper = new backup_nested_element($this->get_recommended_name());
+
+        // Connect our visible container to the parent.
+        $plugin->add_child($pluginwrapper);
+
+        // Define our elements.
+
+        $criteria = new backup_nested_element('guidecriteria');
+
+        $criterion = new backup_nested_element('guidecriterion', array('id'), array(
+            'sortorder', 'shortname', 'description', 'descriptionformat',
+            'descriptionmarkers', 'descriptionmarkersformat', 'maxscore'));
+
+        $comments = new backup_nested_element('guidecomments');
+
+        $comment = new backup_nested_element('guidecomment', array('id'), array(
+            'sortorder', 'description', 'descriptionformat'));
+
+        // Build elements hierarchy.
+
+        $pluginwrapper->add_child($criteria);
+        $criteria->add_child($criterion);
+        $criteria->add_child($comments);
+        $comments->add_child($comment);
+
+        // Set sources to populate the data.
+
+        $criterion->set_source_table('gradingform_guide_criteria',
+                array('definitionid' => backup::VAR_PARENTID));
+
+        $comment->set_source_table('gradingform_guide_comments',
+                array('definitionid' => backup::VAR_PARENTID));
+
+        // No need to annotate ids or files yet (one day when criterion definition supports
+        // embedded files, they must be annotated here).
+
+        return $plugin;
+    }
+
+    /**
+     * Declares marking guide structures to append to the grading form instances
+     * @return backup_plugin_element
+     */
+    protected function define_instance_plugin_structure() {
+
+        // Append data only if the ancestor 'definition' element has 'method' set to 'guide'.
+        $plugin = $this->get_plugin_element(null, '../../../../method', 'guide');
+
+        // Create a visible container for our data.
+        $pluginwrapper = new backup_nested_element($this->get_recommended_name());
+
+        // Connect our visible container to the parent.
+        $plugin->add_child($pluginwrapper);
+
+        // Define our elements.
+
+        $fillings = new backup_nested_element('fillings');
+
+        $filling = new backup_nested_element('filling', array('id'), array(
+            'criterionid', 'remark', 'remarkformat', 'score'));
+
+        // Build elements hierarchy.
+
+        $pluginwrapper->add_child($fillings);
+        $fillings->add_child($filling);
+
+        // Set sources to populate the data.
+
+        $filling->set_source_table('gradingform_guide_fillings',
+            array('instanceid' => backup::VAR_PARENTID));
+
+        // No need to annotate ids or files yet (one day when remark field supports
+        // embedded fileds, they must be annotated here).
+
+        return $plugin;
+    }
+}
diff --git a/grade/grading/form/guide/backup/moodle2/restore_gradingform_guide_plugin.class.php b/grade/grading/form/guide/backup/moodle2/restore_gradingform_guide_plugin.class.php
new file mode 100644 (file)
index 0000000..dd99814
--- /dev/null
@@ -0,0 +1,116 @@
+<?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/>.
+
+/**
+ * Support for restore API
+ *
+ * @package    gradingform_guide
+ * @copyright  2012 Dan Marsden <dan@danmarsden.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Restores the marking guide specific data from grading.xml file
+ *
+ * @package    gradingform_guide
+ * @copyright  2012 Dan Marsden <dan@danmarsden.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class restore_gradingform_guide_plugin extends restore_gradingform_plugin {
+
+    /**
+     * Declares the marking guide XML paths attached to the form definition element
+     *
+     * @return array of {@link restore_path_element}
+     */
+    protected function define_definition_plugin_structure() {
+
+        $paths = array();
+
+        $paths[] = new restore_path_element('gradingform_guide_criterion',
+            $this->get_pathfor('/guidecriteria/guidecriterion'));
+
+        $paths[] = new restore_path_element('gradingform_guide_comment',
+            $this->get_pathfor('/guidecomments/guidecomment'));
+
+        return $paths;
+    }
+
+    /**
+     * Declares the marking guide XML paths attached to the form instance element
+     *
+     * @return array of {@link restore_path_element}
+     */
+    protected function define_instance_plugin_structure() {
+
+        $paths = array();
+
+        $paths[] = new restore_path_element('gradinform_guide_filling',
+            $this->get_pathfor('/fillings/filling'));
+
+        return $paths;
+    }
+
+    /**
+     * Processes criterion element data
+     *
+     * Sets the mapping 'gradingform_guide_criterion' to be used later by
+     * {@link self::process_gradinform_guide_filling()}
+     *
+     * @param array|stdClass $data
+     */
+    public function process_gradingform_guide_criterion($data) {
+        global $DB;
+
+        $data = (object)$data;
+        $oldid = $data->id;
+        $data->definitionid = $this->get_new_parentid('grading_definition');
+
+        $newid = $DB->insert_record('gradingform_guide_criteria', $data);
+        $this->set_mapping('gradingform_guide_criterion', $oldid, $newid);
+    }
+
+    /**
+     * Processes comments element data
+     *
+     * @param array|stdClass $data The data to insert as a comment
+     */
+    public function process_gradingform_guide_comment($data) {
+        global $DB;
+
+        $data = (object)$data;
+        $data->definitionid = $this->get_new_parentid('grading_definition');
+
+        $DB->insert_record('gradingform_guide_comments', $data);
+    }
+
+    /**
+     * Processes filling element data
+     *
+     * @param array|stdClass $data The data to insert as a filling
+     */
+    public function process_gradinform_guide_filling($data) {
+        global $DB;
+
+        $data = (object)$data;
+        $data->instanceid = $this->get_new_parentid('grading_instance');
+        $data->criterionid = $this->get_mappingid('gradingform_guide_criterion', $data->criterionid);
+
+        $DB->insert_record('gradingform_guide_fillings', $data);
+    }
+}
diff --git a/grade/grading/form/guide/db/install.xml b/grade/grading/form/guide/db/install.xml
new file mode 100644 (file)
index 0000000..d8aa0b6
--- /dev/null
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<XMLDB PATH="grade/grading/form/markingguide/db" VERSION="20120404" COMMENT="XMLDB file for Moodle marking guide"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:noNamespaceSchemaLocation="../../../../../lib/xmldb/xmldb.xsd"
+        >
+    <TABLES>
+        <TABLE NAME="gradingform_guide_criteria" COMMENT="Stores the rows of the criteria grid." NEXT="gradingform_guide_fillings">
+            <FIELDS>
+                <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true" NEXT="definitionid"/>
+                <FIELD NAME="definitionid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="The ID of the form definition this criterion is part of" PREVIOUS="id" NEXT="sortorder"/>
+                <FIELD NAME="sortorder" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="Defines the order of the criterion in the guide" PREVIOUS="definitionid" NEXT="shortname"/>
+                <FIELD NAME="shortname" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" COMMENT="shortname of this criterion" PREVIOUS="sortorder" NEXT="description"/>
+                <FIELD NAME="description" TYPE="text" NOTNULL="false" SEQUENCE="false" COMMENT="The criterion description for students" PREVIOUS="shortname" NEXT="descriptionformat"/>
+                <FIELD NAME="descriptionformat" TYPE="int" LENGTH="2" NOTNULL="false" SEQUENCE="false" COMMENT="The format of the description field" PREVIOUS="description" NEXT="descriptionmarkers"/>
+                <FIELD NAME="descriptionmarkers" TYPE="text" NOTNULL="false" SEQUENCE="false" COMMENT="Description for Markers" PREVIOUS="descriptionformat" NEXT="descriptionmarkersformat"/>
+                <FIELD NAME="descriptionmarkersformat" TYPE="int" LENGTH="2" NOTNULL="false" SEQUENCE="false" PREVIOUS="descriptionmarkers" NEXT="maxscore"/>
+                <FIELD NAME="maxscore" TYPE="number" LENGTH="10" NOTNULL="true" SEQUENCE="false" DECIMALS="5" COMMENT="maximum grade that can be assigned using this criterion" PREVIOUS="descriptionmarkersformat"/>
+            </FIELDS>
+            <KEYS>
+                <KEY NAME="primary" TYPE="primary" FIELDS="id" NEXT="fk_definitionid"/>
+                <KEY NAME="fk_definitionid" TYPE="foreign" FIELDS="definitionid" REFTABLE="grading_definitions" REFFIELDS="id" PREVIOUS="primary"/>
+            </KEYS>
+        </TABLE>
+        <TABLE NAME="gradingform_guide_fillings" COMMENT="Stores the data of how the guide is filled by a particular rater" PREVIOUS="gradingform_guide_criteria" NEXT="gradingform_guide_comments">
+            <FIELDS>
+                <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true" NEXT="instanceid"/>
+                <FIELD NAME="instanceid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="The ID of the grading form instance" PREVIOUS="id" NEXT="criterionid"/>
+                <FIELD NAME="criterionid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="The ID of the criterion (row) in the guide" PREVIOUS="instanceid" NEXT="remark"/>
+                <FIELD NAME="remark" TYPE="text" NOTNULL="false" SEQUENCE="false" COMMENT="Side note feedback regarding this particular criterion" PREVIOUS="criterionid" NEXT="remarkformat"/>
+                <FIELD NAME="remarkformat" TYPE="int" LENGTH="2" NOTNULL="false" SEQUENCE="false" COMMENT="The format of the remark field" PREVIOUS="remark" NEXT="score"/>
+                <FIELD NAME="score" TYPE="number" LENGTH="10" NOTNULL="true" SEQUENCE="false" DECIMALS="5" COMMENT="The score assigned" PREVIOUS="remarkformat"/>
+            </FIELDS>
+            <KEYS>
+                <KEY NAME="primary" TYPE="primary" FIELDS="id" NEXT="fk_instanceid"/>
+                <KEY NAME="fk_instanceid" TYPE="foreign" FIELDS="instanceid" REFTABLE="grading_instances" REFFIELDS="id" PREVIOUS="primary" NEXT="fk_criterionid"/>
+                <KEY NAME="fk_criterionid" TYPE="foreign" FIELDS="criterionid" REFTABLE="gradingform_guide_criteria" REFFIELDS="id" PREVIOUS="fk_instanceid" NEXT="uq_instance_criterion"/>
+                <KEY NAME="uq_instance_criterion" TYPE="unique" FIELDS="instanceid, criterionid" PREVIOUS="fk_criterionid"/>
+            </KEYS>
+        </TABLE>
+        <TABLE NAME="gradingform_guide_comments" COMMENT="frequently used comments used in marking guide" PREVIOUS="gradingform_guide_fillings">
+            <FIELDS>
+                <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true" NEXT="definitionid"/>
+                <FIELD NAME="definitionid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="The ID of the form definition this faq is part of" PREVIOUS="id" NEXT="sortorder"/>
+                <FIELD NAME="sortorder" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="Defines the order of the comments" PREVIOUS="definitionid" NEXT="description"/>
+                <FIELD NAME="description" TYPE="text" NOTNULL="false" SEQUENCE="false" COMMENT="The comment description" PREVIOUS="sortorder" NEXT="descriptionformat"/>
+                <FIELD NAME="descriptionformat" TYPE="int" LENGTH="2" NOTNULL="false" SEQUENCE="false" COMMENT="The format of the description field" PREVIOUS="description"/>
+            </FIELDS>
+            <KEYS>
+                <KEY NAME="primary" TYPE="primary" FIELDS="id" NEXT="fk_definitionid"/>
+                <KEY NAME="fk_definitionid" TYPE="foreign" FIELDS="definitionid" REFTABLE="grading_definitions" REFFIELDS="id" PREVIOUS="primary"/>
+            </KEYS>
+        </TABLE>
+    </TABLES>
+</XMLDB>
diff --git a/grade/grading/form/guide/edit.php b/grade/grading/form/guide/edit.php
new file mode 100644 (file)
index 0000000..c4d4810
--- /dev/null
@@ -0,0 +1,62 @@
+<?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/>.
+
+/**
+ * Rubric editor page
+ *
+ * @package    gradingform_guide
+ * @copyright  2012 Dan Marsden <dan@danmarsden.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+require_once(dirname(dirname(dirname(dirname(dirname(__FILE__))))).'/config.php');
+require_once(dirname(__FILE__).'/lib.php');
+require_once(dirname(__FILE__).'/edit_form.php');
+require_once($CFG->dirroot.'/grade/grading/lib.php');
+
+$areaid = required_param('areaid', PARAM_INT);
+
+$manager = get_grading_manager($areaid);
+
+list($context, $course, $cm) = get_context_info_array($manager->get_context()->id);
+
+require_login($course, true, $cm);
+require_capability('moodle/grade:managegradingforms', $context);
+
+$controller = $manager->get_controller('guide');
+
+$PAGE->set_url(new moodle_url('/grade/grading/form/guide/edit.php', array('areaid' => $areaid)));
+$PAGE->set_title(get_string('definemarkingguide', 'gradingform_guide'));
+$PAGE->set_heading(get_string('definemarkingguide', 'gradingform_guide'));
+
+$mform = new gradingform_guide_editguide(null, array('areaid' => $areaid, 'context' => $context,
+    'allowdraft' => !$controller->has_active_instances()), 'post', '', array('class' => 'gradingform_guide_editform'));
+$data = $controller->get_definition_for_editing(true);
+
+$returnurl = optional_param('returnurl', $manager->get_management_url(), PARAM_LOCALURL);
+$data->returnurl = $returnurl;
+$mform->set_data($data);
+if ($mform->is_cancelled()) {
+    redirect($returnurl);
+} else if ($mform->is_submitted() && $mform->is_validated() && !$mform->need_confirm_regrading($controller)) {
+    // Everything ok, validated, re-grading confirmed if needed. Make changes to the rubric.
+    $controller->update_definition($mform->get_data());
+    redirect($returnurl);
+}
+
+echo $OUTPUT->header();
+$mform->display();
+echo $OUTPUT->footer();
\ No newline at end of file
diff --git a/grade/grading/form/guide/edit_form.php b/grade/grading/form/guide/edit_form.php
new file mode 100644 (file)
index 0000000..a133b4b
--- /dev/null
@@ -0,0 +1,220 @@
+<?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/>.
+
+/**
+ * The form used at the guide editor page is defined here
+ *
+ * @package    gradingform_guide
+ * @copyright  2012 Dan Marsden <dan@danmarsden.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+require_once($CFG->dirroot.'/lib/formslib.php');
+require_once(dirname(__FILE__).'/guideeditor.php');
+MoodleQuickForm::registerElementType('guideeditor', $CFG->dirroot.'/grade/grading/form/guide/guideeditor.php',
+    'moodlequickform_guideeditor');
+
+/**
+ * Defines the guide edit form
+ *
+ * @package    gradingform_guide
+ * @copyright  2012 Dan Marsden <dan@danmarsden.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class gradingform_guide_editguide extends moodleform {
+
+    /**
+     * Form element definition
+     */
+    public function definition() {
+        $form = $this->_form;
+
+        $form->addElement('hidden', 'areaid');
+        $form->setType('areaid', PARAM_INT);
+
+        $form->addElement('hidden', 'returnurl');
+
+        // Name.
+        $form->addElement('text', 'name', get_string('name', 'gradingform_guide'), array('size'=>52));
+        $form->addRule('name', get_string('required'), 'required');
+        $form->setType('name', PARAM_TEXT);
+
+        // Description.
+        $options = gradingform_guide_controller::description_form_field_options($this->_customdata['context']);
+        $form->addElement('editor', 'description_editor', get_string('descriptionstudents', 'gradingform_guide'), null, $options);
+        $form->setType('description_editor', PARAM_RAW);
+
+        // Guide completion status.
+        $choices = array();
+        $choices[gradingform_controller::DEFINITION_STATUS_DRAFT]    = html_writer::tag('span',
+            get_string('statusdraft', 'core_grading'), array('class' => 'status draft'));
+        $choices[gradingform_controller::DEFINITION_STATUS_READY]    = html_writer::tag('span',
+            get_string('statusready', 'core_grading'), array('class' => 'status ready'));
+        $form->addElement('select', 'status', get_string('guidestatus', 'gradingform_guide'), $choices)->freeze();
+
+        // Guide editor.
+        $element = $form->addElement('guideeditor', 'guide', get_string('pluginname', 'gradingform_guide'));
+        $form->setType('guide', PARAM_RAW);
+
+        $buttonarray = array();
+        $buttonarray[] = &$form->createElement('submit', 'saveguide', get_string('saveguide', 'gradingform_guide'));
+        if ($this->_customdata['allowdraft']) {
+            $buttonarray[] = &$form->createElement('submit', 'saveguidedraft', get_string('saveguidedraft', 'gradingform_guide'));
+        }
+        $editbutton = &$form->createElement('submit', 'editguide', ' ');
+        $editbutton->freeze();
+        $buttonarray[] = &$editbutton;
+        $buttonarray[] = &$form->createElement('cancel');
+        $form->addGroup($buttonarray, 'buttonar', '', array(' '), false);
+        $form->closeHeaderBefore('buttonar');
+    }
+
+    /**
+     * Setup the form depending on current values. This method is called after definition(),
+     * data submission and set_data().
+     * All form setup that is dependent on form values should go in here.
+     *
+     * We remove the element status if there is no current status (i.e. guide is only being created)
+     * so the users do not get confused
+     */
+    public function definition_after_data() {
+        $form = $this->_form;
+        $el = $form->getElement('status');
+        if (!$el->getValue()) {
+            $form->removeElement('status');
+        } else {
+            $vals = array_values($el->getValue());
+            if ($vals[0] == gradingform_controller::DEFINITION_STATUS_READY) {
+                $this->findbutton('saveguide')->setValue(get_string('save', 'gradingform_guide'));
+            }
+        }
+    }
+
+    /**
+     * Form vlidation.
+     * If there are errors return array of errors ("fieldname"=>"error message"),
+     * otherwise true if ok.
+     *
+     * @param array $data array of ("fieldname"=>value) of submitted data
+     * @param array $files array of uploaded files "element_name"=>tmp_file_path
+     * @return array of "element_name"=>"error_description" if there are errors,
+     *               or an empty array if everything is OK (true allowed for backwards compatibility too).
+     */
+    public function validation($data, $files) {
+        $err = parent::validation($data, $files);
+        $err = array();
+        $form = $this->_form;
+        $guideel = $form->getElement('guide');
+        if ($guideel->non_js_button_pressed($data['guide'])) {
+            // If JS is disabled and button such as 'Add criterion' is pressed - prevent from submit.
+            $err['guidedummy'] = 1;
+        } else if (isset($data['editguide'])) {
+            // Continue editing.
+            $err['guidedummy'] = 1;
+        } else if ((isset($data['saveguide']) && $data['saveguide']) ||
+                   (isset($data['saveguidedraft']) && $data['saveguidedraft'])) {
+            // If user attempts to make guide active - it needs to be validated.
+            if ($guideel->validate($data['guide']) !== false) {
+                $err['guidedummy'] = 1;
+            }
+        }
+        return $err;
+    }
+
+    /**
+     * Return submitted data if properly submitted or returns NULL if validation fails or
+     * if there is no submitted data.
+     *
+     * @return object submitted data; NULL if not valid or not submitted or cancelled
+     */
+    public function get_data() {
+        $data = parent::get_data();
+        if (!empty($data->saveguide)) {
+            $data->status = gradingform_controller::DEFINITION_STATUS_READY;
+        } else if (!empty($data->saveguidedraft)) {
+            $data->status = gradingform_controller::DEFINITION_STATUS_DRAFT;
+        }
+        return $data;
+    }
+
+    /**
+     * Check if there are changes in the guide and it is needed to ask user whether to
+     * mark the current grades for re-grading. User may confirm re-grading and continue,
+     * return to editing or cancel the changes
+     *
+     * @param gradingform_guide_controller $controller
+     */
+    public function need_confirm_regrading($controller) {
+        $data = $this->get_data();
+        if (isset($data->guide['regrade'])) {
+            // We have already displayed the confirmation on the previous step.
+            return false;
+        }
+        if (!isset($data->saveguide) || !$data->saveguide) {
+            // We only need confirmation when button 'Save guide' is pressed.
+            return false;
+        }
+        if (!$controller->has_active_instances()) {
+            // Nothing to re-grade, confirmation not needed.
+            return false;
+        }
+        $changelevel = $controller->update_or_check_guide($data);
+        if ($changelevel == 0) {
+            // No changes in the guide, no confirmation needed.
+            return false;
+        }
+
+        // Freeze form elements and pass the values in hidden fields.
+        // TODO description_editor does not freeze the normal way!
+        $form = $this->_form;
+        foreach (array('guide', 'name'/*, 'description_editor'*/) as $fieldname) {
+            $el =& $form->getElement($fieldname);
+            $el->freeze();
+            $el->setPersistantFreeze(true);
+            if ($fieldname == 'guide') {
+                $el->add_regrade_confirmation($changelevel);
+            }
+        }
+
+        // Replace button text 'saveguide' and unfreeze 'Back to edit' button.
+        $this->findbutton('saveguide')->setValue(get_string('continue'));
+        $el =& $this->findbutton('editguide');
+        $el->setValue(get_string('backtoediting', 'gradingform_guide'));
+        $el->unfreeze();
+
+        return true;
+    }
+
+    /**
+     * Returns a form element (submit button) with the name $elementname
+     *
+     * @param string $elementname
+     * @return HTML_QuickForm_element
+     */
+    protected function &findbutton($elementname) {
+        $form = $this->_form;
+        $buttonar =& $form->getElement('buttonar');
+        $elements =& $buttonar->getElements();
+        foreach ($elements as $el) {
+            if ($el->getName() == $elementname) {
+                return $el;
+            }
+        }
+        return null;
+    }
+}
diff --git a/grade/grading/form/guide/guideeditor.php b/grade/grading/form/guide/guideeditor.php
new file mode 100644 (file)
index 0000000..aebb982
--- /dev/null
@@ -0,0 +1,356 @@
+<?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/>.
+
+/**
+ * This file contains the marking guide editor element
+ *
+ * @package    gradingform_guide
+ * @copyright  2012 Dan Marsden <dan@danmarsden.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+require_once("HTML/QuickForm/input.php");
+
+/**
+ * The editor for the marking guide advanced grading plugin.
+ *
+ * @package    gradingform_guide
+ * @copyright  2012 Dan Marsden <dan@danmarsden.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class moodlequickform_guideeditor extends HTML_QuickForm_input {
+    /** @var string help message */
+    public $_helpbutton = '';
+    /** @var null|false|string stores the result of the last validation: null - undefined, false - no errors,
+     * string - error(s) text */
+    protected $validationerrors = null;
+    /** @var bool if element has already been validated **/
+    protected $wasvalidated = false;
+    /** @var null|bool If non-submit (JS) button was pressed: null - unknown, true/false - button was/wasn't pressed */
+    protected $nonjsbuttonpressed = false;
+    /** @var string|false Message to display in front of the editor (that there exist grades on this guide being edited) */
+    protected $regradeconfirmation = false;
+
+    /**
+     * Constructor
+     *
+     * @param string $elementname
+     * @param string $elementlabel
+     * @param array $attributes
+     */
+    public function moodlequickform_guideeditor($elementname=null, $elementlabel=null, $attributes=null) {
+        parent::HTML_QuickForm_input($elementname, $elementlabel, $attributes);
+    }
+
+    /**
+     * get html for help button
+     *
+     * @return  string html for help button
+     */
+    public function getHelpButton() {
+        return $this->_helpbutton;
+    }
+
+    /**
+     * The renderer will take care itself about different display in normal and frozen states
+     *
+     * @return string
+     */
+    public function getElementTemplateType() {
+        return 'default';
+    }
+
+    /**
+     * Specifies that confirmation about re-grading needs to be added to this rubric editor.
+     * $changelevel is saved in $this->regradeconfirmation and retrieved in toHtml()
+     *
+     * @see gradingform_rubric_controller::update_or_check_rubric()
+     * @param int $changelevel
+     */
+    public function add_regrade_confirmation($changelevel) {
+        $this->regradeconfirmation = $changelevel;
+    }
+
+    /**
+     * Returns html string to display this element
+     *
+     * @return string
+     */
+    public function toHtml() {
+        global $PAGE;
+        $html = $this->_getTabs();
+        $renderer = $PAGE->get_renderer('gradingform_guide');
+        $data = $this->prepare_data(null, $this->wasvalidated);
+        if (!$this->_flagFrozen) {
+            $mode = gradingform_guide_controller::DISPLAY_EDIT_FULL;
+            $module = array('name'=>'gradingform_guideeditor',
+                'fullpath'=>'/grade/grading/form/guide/js/guideeditor.js',
+                'strings' => array(
+                    array('confirmdeletecriterion', 'gradingform_guide'),
+                    array('clicktoedit', 'gradingform_guide'),
+                    array('clicktoeditname', 'gradingform_guide')
+            ));
+            $PAGE->requires->js_init_call('M.gradingform_guideeditor.init', array(
+                array('name' => $this->getName(),
+                    'criteriontemplate' => $renderer->criterion_template($mode, $data['options'], $this->getName()),
+                    'commenttemplate' => $renderer->comment_template($mode, $this->getName())
+                   )),
+                true, $module);
+        } else {
+            // Guide is frozen, no javascript needed.
+            if ($this->_persistantFreeze) {
+                $mode = gradingform_guide_controller::DISPLAY_EDIT_FROZEN;
+            } else {
+                $mode = gradingform_guide_controller::DISPLAY_PREVIEW;
+            }
+        }
+        if ($this->regradeconfirmation) {
+            if (!isset($data['regrade'])) {
+                $data['regrade'] = 1;
+            }
+            $html .= $renderer->display_regrade_confirmation($this->getName(), $this->regradeconfirmation, $data['regrade']);
+        }
+        if ($this->validationerrors) {
+            $html .= $renderer->notification($this->validationerrors, 'error');
+        }
+        $html .= $renderer->display_guide($data['criteria'], $data['comments'], $data['options'], $mode, $this->getName());
+        return $html;
+    }
+    /**
+     * Prepares the data passed in $_POST:
+     * - processes the pressed buttons 'addlevel', 'addcriterion', 'moveup', 'movedown', 'delete' (when JavaScript is disabled)
+     *   sets $this->nonjsbuttonpressed to true/false if such button was pressed
+     * - if options not passed (i.e. we create a new guide) fills the options array with the default values
+     * - if options are passed completes the options array with unchecked checkboxes
+     * - if $withvalidation is set, adds 'error_xxx' attributes to elements that contain errors and creates an error string
+     *   and stores it in $this->validationerrors
+     *
+     * @param array $value
+     * @param boolean $withvalidation whether to enable data validation
+     * @return array
+     */
+    protected function prepare_data($value = null, $withvalidation = false) {
+        if (null === $value) {
+            $value = $this->getValue();
+        }
+        if ($this->nonjsbuttonpressed === null) {
+            $this->nonjsbuttonpressed = false;
+        }
+
+        $errors = array();
+        $return = array('criteria' => array(), 'options' => gradingform_guide_controller::get_default_options(),
+            'comments' => array());
+        if (!isset($value['criteria'])) {
+            $value['criteria'] = array();
+            $errors['err_nocriteria'] = 1;
+        }
+        // If options are present in $value, replace default values with submitted values.
+        if (!empty($value['options'])) {
+            foreach (array_keys($return['options']) as $option) {
+                // Special treatment for checkboxes.
+                if (!empty($value['options'][$option])) {
+                    $return['options'][$option] = $value['options'][$option];
+                } else {
+                    $return['options'][$option] = null;
+                }
+
+            }
+        }
+
+        if (is_array($value)) {
+            // For other array keys of $value no special treatmeant neeeded, copy them to return value as is.
+            foreach (array_keys($value) as $key) {
+                if ($key != 'options' && $key != 'criteria' && $key != 'comments') {
+                    $return[$key] = $value[$key];
+                }
+            }
+        }
+
+        // Iterate through criteria.
+        $lastaction = null;
+        $lastid = null;
+        foreach ($value['criteria'] as $id => $criterion) {
+            if ($id == 'addcriterion') {
+                $id = $this->get_next_id(array_keys($value['criteria']));
+                $criterion = array('description' => '');
+                $this->nonjsbuttonpressed = true;
+            }
+
+            if ($withvalidation && !array_key_exists('delete', $criterion)) {
+                if (!strlen(trim($criterion['shortname']))) {
+                    $errors['err_noshortname'] = 1;
+                    $criterion['error_description'] = true;
+                }
+                if (!strlen(trim($criterion['maxscore']))) {
+                    $errors['err_nomaxscore'] = 1;
+                    $criterion['error_description'] = true;
+                } else if (!is_numeric($criterion['maxscore']) || $criterion['maxscore'] < 0) {
+                    $errors['err_maxscorenotnumeric'] = 1;
+                    $criterion['error_description'] = true;
+                }
+            }
+            if (array_key_exists('moveup', $criterion) || $lastaction == 'movedown') {
+                unset($criterion['moveup']);
+                if ($lastid !== null) {
+                    $lastcriterion = $return['criteria'][$lastid];
+                    unset($return['criteria'][$lastid]);
+                    $return['criteria'][$id] = $criterion;
+                    $return['criteria'][$lastid] = $lastcriterion;
+                } else {
+                    $return['criteria'][$id] = $criterion;
+                }
+                $lastaction = null;
+                $lastid = $id;
+                $this->nonjsbuttonpressed = true;
+            } else if (array_key_exists('delete', $criterion)) {
+                $this->nonjsbuttonpressed = true;
+            } else {
+                if (array_key_exists('movedown', $criterion)) {
+                    unset($criterion['movedown']);
+                    $lastaction = 'movedown';
+                    $this->nonjsbuttonpressed = true;
+                }
+                $return['criteria'][$id] = $criterion;
+                $lastid = $id;
+            }
+        }
+
+        // Add sort order field to criteria.
+        $csortorder = 1;
+        foreach (array_keys($return['criteria']) as $id) {
+            $return['criteria'][$id]['sortorder'] = $csortorder++;
+        }
+
+        // Iterate through comments.
+        $lastaction = null;
+        $lastid = null;
+        if (!empty($value['comments'])) {
+            foreach ($value['comments'] as $id => $comment) {
+                if ($id == 'addcomment') {
+                    $id = $this->get_next_id(array_keys($value['comments']));
+                    $comment = array('description' => '');
+                    $this->nonjsbuttonpressed = true;
+                }
+
+                if (array_key_exists('moveup', $comment) || $lastaction == 'movedown') {
+                    unset($comment['moveup']);
+                    if ($lastid !== null) {
+                        $lastcomment = $return['comments'][$lastid];
+                        unset($return['comments'][$lastid]);
+                        $return['comments'][$id] = $comment;
+                        $return['comments'][$lastid] = $lastcomment;
+                    } else {
+                        $return['comments'][$id] = $comment;
+                    }
+                    $lastaction = null;
+                    $lastid = $id;
+                    $this->nonjsbuttonpressed = true;
+                } else if (array_key_exists('delete', $comment)) {
+                    $this->nonjsbuttonpressed = true;
+                } else {
+                    if (array_key_exists('movedown', $comment)) {
+                        unset($comment['movedown']);
+                        $lastaction = 'movedown';
+                        $this->nonjsbuttonpressed = true;
+                    }
+                    $return['comments'][$id] = $comment;
+                    $lastid = $id;
+                }
+            }
+            // Add sort order field to comments.
+            $csortorder = 1;
+            foreach (array_keys($return['comments']) as $id) {
+                $return['comments'][$id]['sortorder'] = $csortorder++;
+            }
+        }
+        // Create validation error string (if needed).
+        if ($withvalidation) {
+            if (count($errors)) {
+                $rv = array();
+                foreach ($errors as $error => $v) {
+                    $rv[] = get_string($error, 'gradingform_guide');
+                }
+                $this->validationerrors = join('<br/ >', $rv);
+            } else {
+                $this->validationerrors = false;
+            }
+            $this->wasvalidated = true;
+        }
+        return $return;
+
+    }
+
+    /**
+     * Scans array $ids to find the biggest element ! NEWID*, increments it by 1 and returns
+     *
+     * @param array $ids
+     * @return string
+     */
+    protected function get_next_id($ids) {
+        $maxid = 0;
+        foreach ($ids as $id) {
+            if (preg_match('/^NEWID(\d+)$/', $id, $matches) && ((int)$matches[1]) > $maxid) {
+                $maxid = (int)$matches[1];
+            }
+        }
+        return 'NEWID'.($maxid+1);
+    }
+
+    /**
+     * Checks if a submit button was pressed which is supposed to be processed on client side by JS
+     * but user seem to have disabled JS in the browser.
+     * (buttons 'add criteria', 'add level', 'move up', 'move down', 'add comment')
+     * In this case the form containing this element is prevented from being submitted
+     *
+     * @param array $value
+     * @return boolean true if non-submit button was pressed and not processed by JS
+     */
+    public function non_js_button_pressed($value) {
+        if ($this->nonjsbuttonpressed === null) {
+            $this->prepare_data($value);
+        }
+        return $this->nonjsbuttonpressed;
+    }
+
+    /**
+     * Validates that guide has at least one criterion, filled definitions and all criteria
+     * have filled descriptions
+     *
+     * @param array $value
+     * @return string|false error text or false if no errors found
+     */
+    public function validate($value) {
+        if (!$this->wasvalidated) {
+            $this->prepare_data($value, true);
+        }
+        return $this->validationerrors;
+    }
+
+    /**
+     * Prepares the data for saving
+     * @see prepare_data()
+     *
+     * @param array $submitvalues
+     * @param boolean $assoc
+     * @return array
+     */
+    public function exportValue(&$submitvalues, $assoc = false) {
+        $value =  $this->prepare_data($this->_findValue($submitvalues));
+        return $this->_prepareValue($value, $assoc);
+    }
+}
diff --git a/grade/grading/form/guide/js/guide.js b/grade/grading/form/guide/js/guide.js
new file mode 100644 (file)
index 0000000..783ee46
--- /dev/null
@@ -0,0 +1,32 @@
+M.gradingform_guide = {};
+
+/**
+ * This function is called for each guide on page.
+ */
+M.gradingform_guide.init = function(Y, options) {
+    var currentfocus = null;
+
+    Y.all('.markingguideremark').on('blur', function(e) {
+        currentfocus = e.currentTarget;
+    });
+    Y.all('.markingguidecomment').on('click', function(e) {
+        currentfocus.set('value', currentfocus.get('value') + '\n' + e.currentTarget.get('innerHTML'));
+        currentfocus.focus();
+    });
+
+    Y.all('.showmarkerdesc input[type=radio]').on('click', function(e) {
+        if (e.currentTarget.get('value')=='false') {
+            Y.all('.criteriondescriptionmarkers').addClass('hide');
+        } else {
+            Y.all('.criteriondescriptionmarkers').removeClass('hide');
+        }
+    });
+
+    Y.all('.showstudentdesc input[type=radio]').on('click', function(e) {
+        if (e.currentTarget.get('value')=='false') {
+            Y.all('.criteriondescription').addClass('hide');
+        } else {
+            Y.all('.criteriondescription').removeClass('hide');
+        }
+    });
+};
\ No newline at end of file
diff --git a/grade/grading/form/guide/js/guideeditor.js b/grade/grading/form/guide/js/guideeditor.js
new file mode 100644 (file)
index 0000000..3aeb83b
--- /dev/null
@@ -0,0 +1,252 @@
+M.gradingform_guideeditor = {'templates' : {}, 'eventhandler' : null, 'name' : null, 'Y' : null};
+
+/**
+ * This function is called for each guideeditor on page.
+ */
+M.gradingform_guideeditor.init = function(Y, options) {
+    M.gradingform_guideeditor.name = options.name
+    M.gradingform_guideeditor.Y = Y
+    M.gradingform_guideeditor.templates[options.name] = {
+        'criterion' : options.criteriontemplate,
+        'comment' : options.commenttemplate
+    }
+    M.gradingform_guideeditor.disablealleditors()
+    Y.on('click', M.gradingform_guideeditor.clickanywhere, 'body', null)
+    YUI().use('event-touch', function (Y) {
+        Y.one('body').on('touchstart', M.gradingform_guideeditor.clickanywhere);
+        Y.one('body').on('touchend', M.gradingform_guideeditor.clickanywhere);
+    })
+    M.gradingform_guideeditor.addhandlers()
+};
+
+// Adds handlers for clicking submit button. This function must be called each time JS adds new elements to html
+M.gradingform_guideeditor.addhandlers = function() {
+    var Y = M.gradingform_guideeditor.Y
+    var name = M.gradingform_guideeditor.name
+    if (M.gradingform_guideeditor.eventhandler) {
+        M.gradingform_guideeditor.eventhandler.detach()
+    }
+    M.gradingform_guideeditor.eventhandler = Y.on('click', M.gradingform_guideeditor.buttonclick, '#guide-'+name+' input[type=submit]', null);
+}
+
+// switches all input text elements to non-edit mode
+M.gradingform_guideeditor.disablealleditors = function() {
+    var Y = M.gradingform_guideeditor.Y
+    var name = M.gradingform_guideeditor.name
+    Y.all('#guide-'+name+' .criteria .description input[type=text]:not(.pseudotablink)').each( function(node) {M.gradingform_guideeditor.editmode(node, false)} );
+    Y.all('#guide-'+name+' .criteria .description textarea').each( function(node) {M.gradingform_guideeditor.editmode(node, false)} );
+    Y.all('#guide-'+name+' .comments .description textarea').each( function(node) {M.gradingform_guideeditor.editmode(node, false)} );
+}
+
+// function invoked on each click on the page. If criterion values are clicked
+// it switches the element to edit mode. If guide button is clicked it does nothing so the 'buttonclick'
+// function is invoked
+M.gradingform_guideeditor.clickanywhere = function(e) {
+    if (e.type == 'touchstart') {
+        return
+    }
+    var el = e.target
+    // if clicked on button - disablecurrenteditor, continue
+    if (el.get('tagName') == 'INPUT' && el.get('type') == 'submit') {
+        return
+    }
+    // if clicked on description item and this item is not enabled - enable it
+    var container = null
+    if ((container = el.ancestor('.criterionname')) || (container = el.ancestor('.criterionmaxscore'))) {
+        el = container.one('input[type=text]')
+    } else if ((container = el.ancestor('.criteriondesc')) || (container = el.ancestor('.criteriondescmarkers'))) {
+        el = container.one('textarea')
+    } else {
+        el = null
+    }
+    if (el) {
+        if (el.hasClass('hiddenelement')) {
+            M.gradingform_guideeditor.disablealleditors()
+            M.gradingform_guideeditor.editmode(el, true)
+        }
+        return
+    }
+    // else disablecurrenteditor
+    M.gradingform_guideeditor.disablealleditors()
+}
+
+// switch the criterion item to edit mode or switch back
+M.gradingform_guideeditor.editmode = function(el, editmode) {
+    var Y = M.gradingform_guideeditor.Y
+    var ta = el
+    if (!editmode && ta.hasClass('hiddenelement')) {
+        return;
+    }
+    if (editmode && !ta.hasClass('hiddenelement')) {
+        return;
+    }
+    var pseudotablink = '<input type="text" size="1" class="pseudotablink"/>',
+        taplain = ta.next('.plainvalue'),
+        tbplain = null,
+        tb = el.one('.score input[type=text]')
+    // add 'plainvalue' next to textarea for description/definition and next to input text field for score (if applicable)
+    if (!taplain && ta.get('name') != '') {
+        ta.insert('<div class="plainvalue">'+pseudotablink+'<span class="textvalue">&nbsp;</span></div>', 'after')
+        taplain = ta.next('.plainvalue')
+        taplain.one('.pseudotablink').on('focus', M.gradingform_guideeditor.clickanywhere)
+        if (tb) {
+            tb.get('parentNode').append('<span class="plainvalue">'+pseudotablink+'<span class="textvalue">&nbsp;</span></span>')
+            tbplain = tb.get('parentNode').one('.plainvalue')
+            tbplain.one('.pseudotablink').on('focus', M.gradingform_guideeditor.clickanywhere)
+        }
+    }
+    if (tb && !tbplain) {
+        tbplain = tb.get('parentNode').one('.plainvalue')
+    }
+    if (!editmode) {
+        // if we need to hide the input fields, copy their contents to plainvalue(s). If description/definition
+        // is empty, display the default text ('Click to edit ...') and add/remove 'empty' CSS class to element
+        var value = Y.Lang.trim(ta.get('value'));
+        if (value.length) {
+            taplain.removeClass('empty')
+        } else if (ta.get('name').indexOf('[shortname]') > 1){
+            value = M.str.gradingform_guide.clicktoeditname
+            taplain.addClass('editname')
+        } else {
+            value = M.str.gradingform_guide.clicktoedit
+            taplain.addClass('empty')
+        }
+        taplain.one('.textvalue').set('innerHTML', value)
+        if (tb) {
+            tbplain.one('.textvalue').set('innerHTML', tb.get('value'))
+        }
+        // hide/display textarea, textbox and plaintexts
+        taplain.removeClass('hiddenelement')
+        ta.addClass('hiddenelement')
+        if (tb) {
+            tbplain.removeClass('hiddenelement')
+            tb.addClass('hiddenelement')
+        }
+    } else {
+        // if we need to show the input fields, set the width/height for textarea so it fills the cell
+        try {
+            if (ta.get('name').indexOf('[maxscore]') > 1) {
+                ta.setStyle('width', '25px');
+            } else {
+                var width = parseFloat(ta.get('parentNode').getComputedStyle('width'))-10,
+                    height = parseFloat(ta.get('parentNode').getComputedStyle('height'))
+                ta.setStyle('width', Math.max(width,50)+'px')
+                ta.setStyle('height', Math.max(height,30)+'px')
+            }
+        }
+        catch (err) {
+            // this browser do not support 'computedStyle', leave the default size of the textbox
+        }
+        // hide/display textarea, textbox and plaintexts
+        taplain.addClass('hiddenelement')
+        ta.removeClass('hiddenelement')
+        if (tb) {
+            tbplain.addClass('hiddenelement')
+            tb.removeClass('hiddenelement')
+        }
+    }
+    // focus the proper input field in edit mode
+    if (editmode) {
+        ta.focus()
+    }
+}
+
+// handler for clicking on submit buttons within guideeditor element. Adds/deletes/rearranges criteria/comments on client side
+M.gradingform_guideeditor.buttonclick = function(e, confirmed) {
+    var Y = M.gradingform_guideeditor.Y
+    var name = M.gradingform_guideeditor.name
+    if (e.target.get('type') != 'submit') {
+        return;
+    }
+    M.gradingform_guideeditor.disablealleditors()
+    var chunks = e.target.get('id').split('-')
+    var section = chunks[1]
+    var action = chunks[chunks.length-1]
+
+    if (chunks[0] != name || (section != 'criteria' && section != 'comments')) {
+        return;
+    }
+    // prepare the id of the next inserted criterion
+
+    if (section == 'criteria') {
+        elements_str = '#guide-'+name+' .criteria .criterion'
+    } else if (section == 'comments') {
+        elements_str = '#guide-'+name+' .comments .criterion'
+    }
+    if (action == 'addcriterion' || action == 'addcomment') {
+        var newid = M.gradingform_guideeditor.calculatenewid(elements_str)
+    }
+    var dialog_options = {
+        'scope' : this,
+        'callbackargs' : [e, true],
+        'callback' : M.gradingform_guideeditor.buttonclick
+    };
+    if (chunks.length == 3 && (action == 'addcriterion' || action == 'addcomment')) {
+        // ADD NEW CRITERION OR COMMENT
+        var parentel = Y.one('#'+name+'-'+section)
+        if (parentel.one('>tbody')) {
+            parentel = parentel.one('>tbody')
+        }
+        if (section == 'criteria') {
+            var newcriterion = M.gradingform_guideeditor.templates[name]['criterion']
+            parentel.append(newcriterion.replace(/\{CRITERION-id\}/g, 'NEWID'+newid).replace(/\{.+?\}/g, ''))
+        } else if (section == 'comments') {
+            var newcomment = M.gradingform_guideeditor.templates[name]['comment']
+            parentel.append(newcomment.replace(/\{COMMENT-id\}/g, 'NEWID'+newid).replace(/\{.+?\}/g, ''))
+        }
+
+        M.gradingform_guideeditor.addhandlers();
+        M.gradingform_guideeditor.disablealleditors()
+        M.gradingform_guideeditor.assignclasses(elements_str)
+        //M.gradingform_guideeditor.editmode(Y.one('#guide-'+name+' #'+name+'-'+section+'-NEWID'+newid+'-shortname'),true)
+    } else if (chunks.length == 4 && action == 'moveup') {
+        // MOVE UP
+        el = Y.one('#'+name+'-'+section+'-'+chunks[2])
+        if (el.previous()) {
+            el.get('parentNode').insertBefore(el, el.previous())
+        }
+        M.gradingform_guideeditor.assignclasses(elements_str)
+    } else if (chunks.length == 4 && action == 'movedown') {
+        // MOVE DOWN
+        el = Y.one('#'+name+'-'+section+'-'+chunks[2])
+        if (el.next()) {
+            el.get('parentNode').insertBefore(el.next(), el)
+        }
+        M.gradingform_guideeditor.assignclasses(elements_str)
+    } else if (chunks.length == 4 && action == 'delete') {
+        // DELETE
+        if (confirmed) {
+            Y.one('#'+name+'-'+section+'-'+chunks[2]).remove()
+            M.gradingform_guideeditor.assignclasses(elements_str)
+        } else {
+            dialog_options['message'] = M.str.gradingform_guide.confirmdeletecriterion
+            M.util.show_confirm_dialog(e, dialog_options);
+        }
+    } else {
+        // unknown action
+        return;
+    }
+    e.preventDefault();
+}
+
+// properly set classes (first/last/odd/even) and/or criterion sortorder for elements Y.all(elements_str)
+M.gradingform_guideeditor.assignclasses = function (elements_str) {
+    var elements = M.gradingform_guideeditor.Y.all(elements_str)
+    for (var i=0; i<elements.size(); i++) {
+        elements.item(i).removeClass('first').removeClass('last').removeClass('even').removeClass('odd').
+            addClass(((i%2)?'odd':'even') + ((i==0)?' first':'') + ((i==elements.size()-1)?' last':''))
+        elements.item(i).all('input[type=hidden]').each(
+            function(node) {if (node.get('name').match(/sortorder/)) { node.set('value', i)}}
+        );
+    }
+}
+
+// returns unique id for the next added element, it should not be equal to any of Y.all(elements_str) ids
+M.gradingform_guideeditor.calculatenewid = function (elements_str) {
+    var newid = 1
+    M.gradingform_guideeditor.Y.all(elements_str).each( function(node) {
+        var idchunks = node.get('id').split('-'), id = idchunks.pop();
+        if (id.match(/^NEWID(\d+)$/)) { newid = Math.max(newid, parseInt(id.substring(5))+1)};
+    } );
+    return newid
+}
diff --git a/grade/grading/form/guide/lang/en/gradingform_guide.php b/grade/grading/form/guide/lang/en/gradingform_guide.php
new file mode 100644 (file)
index 0000000..77c1391
--- /dev/null
@@ -0,0 +1,82 @@
+<?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/>.
+
+/**
+ * Strings for the marking guide advanced grading plugin
+ *
+ * @package    gradingform_guide
+ * @copyright  2012 Dan Marsden <dan@danmarsden.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+$string['addcomment'] = 'Add frequently used comment';
+$string['addcriterion'] = 'Add criterion';
+$string['alwaysshowdefinition'] = 'Show guide definition to students';
+$string['backtoediting'] = 'Back to editing';
+$string['clicktocopy'] = 'Click to copy this text into the criteria feedback';
+$string['clicktoedit'] = 'Click to edit';
+$string['clicktoeditname'] = 'Click to edit criterion name';
+$string['comments'] = 'Frequently used comments';
+$string['commentsdelete'] = 'Delete comment';
+$string['commentsempty'] = 'Click to edit comment';
+$string['commentsmovedown'] = 'Move down';
+$string['commentsmoveup'] = 'Move up';
+$string['confirmdeletecriterion'] = 'Are you sure you want to delete this item?';
+$string['confirmdeletelevel'] = 'Are you sure you want to delete this level?';
+$string['criterion'] = 'Criterion';
+$string['criteriondelete'] = 'Delete criterion';
+$string['criterionempty'] = 'Click to edit criterion';
+$string['criterionmovedown'] = 'Move down';
+$string['criterionmoveup'] = 'Move up';
+$string['definemarkingguide'] = 'Define marking guide';
+$string['description'] = 'Description';
+$string['descriptionmarkers'] = 'Description for Markers';
+$string['descriptionstudents'] = 'Description for Students';
+$string['err_maxscorenotnumeric'] = 'Criterion max score must be numeric';
+$string['err_nocomment'] = 'Comment can not be empty';
+$string['err_nodescription'] = 'Student description can not be empty';
+$string['err_nodescriptionmarkers'] = 'Marker description can not be empty';
+$string['err_nomaxscore'] = 'Criterion max score can not be empty';
+$string['err_noshortname'] = 'Criterion name can not be empty';
+$string['err_scoreinvalid'] = 'The score given to {$a->criterianame} is not valid, the max score is: {$a->maxscore}';
+$string['gradingof'] = '{$a} grading';
+$string['guidemappingexplained'] = 'WARNING: Your marking guide has a maximum grade of <b>{$a->maxscore} points</b>┬ábut the maximum grade set in your activity is {$a->modulegrade}  The maximum score set in your marking guide will be scaled up to the maximum grade in the module.<br />
+    Intermediate scores will be converted respectively and rounded to the nearest available grade.';
+$string['guidenotcompleted'] = 'Please provide a valid grade for each criterion';
+$string['guideoptions'] = 'Marking guide options';
+$string['guidestatus'] = 'Current marking guide status';
+$string['hidemarkerdesc'] = 'Hide marker criterion descriptions';
+$string['hidestudentdesc'] = 'Hide student criterion descriptions';
+$string['maxscore'] = 'Maximum mark';
+$string['name'] = 'Name';
+$string['needregrademessage'] = 'The marking guide definition was changed after this student had been graded. The student can not see this marking guide until you check the marking guide and update the grade.';
+$string['pluginname'] = 'Marking guide';
+$string['previewmarkingguide'] = 'Preview marking guide';
+$string['regrademessage1'] = 'You are about to save changes to a marking guide that has already been used for grading. Please indicate if existing grades need to be reviewed. If you set this then the marking guide will be hidden from students until their item is regraded.';
+$string['regrademessage5'] = 'You are about to save significant changes to a marking guide that has already been used for grading. The gradebook value will be unchanged, but the marking guide will be hidden from students until their item is regraded.';
+$string['regradeoption0'] = 'Do not mark for regrade';
+$string['regradeoption1'] = 'Mark for regrade';
+$string['restoredfromdraft'] = 'NOTE: The last attempt to grade this person was not saved properly so draft grades have been restored. If you want to cancel these changes use the \'Cancel\' button below.';
+$string['save'] = 'Save';
+$string['saveguide'] = 'Save marking guide and make it ready';
+$string['saveguidedraft'] = 'Save as draft';
+$string['score'] = 'score';
+$string['showdescriptionstudent'] = 'Display description to those being graded';
+$string['showmarkerdesc'] = 'Show marker criterion descriptions';
+$string['showmarkspercriterionstudents'] = 'Show marks per criterion to students';
+$string['showstudentdesc'] = 'Show student criterion descriptions';
diff --git a/grade/grading/form/guide/lib.php b/grade/grading/form/guide/lib.php
new file mode 100644 (file)
index 0000000..8cb164a
--- /dev/null
@@ -0,0 +1,880 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Grading method controller for the guide plugin
+ *
+ * @package    gradingform_guide
+ * @copyright  2012 Dan Marsden <dan@danmarsden.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+require_once($CFG->dirroot.'/grade/grading/form/lib.php');
+
+/**
+ * This controller encapsulates the guide grading logic
+ *
+ * @package    gradingform_guide
+ * @copyright  2012 Dan Marsden <dan@danmarsden.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class gradingform_guide_controller extends gradingform_controller {
+    // Modes of displaying the guide (used in gradingform_guide_renderer).
+    /** guide display mode: For editing (moderator or teacher creates a guide) */
+    const DISPLAY_EDIT_FULL     = 1;
+    /** guide display mode: Preview the guide design with hidden fields */
+    const DISPLAY_EDIT_FROZEN   = 2;
+    /** guide display mode: Preview the guide design (for person with manage permission) */
+    const DISPLAY_PREVIEW       = 3;
+    /** guide display mode: Preview the guide (for people being graded) */
+    const DISPLAY_PREVIEW_GRADED= 8;
+    /** guide display mode: For evaluation, enabled (teacher grades a student) */
+    const DISPLAY_EVAL          = 4;
+    /** guide display mode: For evaluation, with hidden fields */
+    const DISPLAY_EVAL_FROZEN   = 5;
+    /** guide display mode: Teacher reviews filled guide */
+    const DISPLAY_REVIEW        = 6;
+    /** guide display mode: Dispaly filled guide (i.e. students see their grades) */
+    const DISPLAY_VIEW          = 7;
+
+    /** @var stdClass|false the definition structure */
+    protected $moduleinstance = false;
+
+    /**
+     * Extends the module settings navigation with the guide grading settings
+     *
+     * This function is called when the context for the page is an activity module with the
+     * FEATURE_ADVANCED_GRADING, the user has the permission moodle/grade:managegradingforms
+     * and there is an area with the active grading method set to 'guide'.
+     *
+     * @param settings_navigation $settingsnav {@link settings_navigation}
+     * @param navigation_node $node {@link navigation_node}
+     */
+    public function extend_settings_navigation(settings_navigation $settingsnav, navigation_node $node=null) {
+        $node->add(get_string('definemarkingguide', 'gradingform_guide'),
+            $this->get_editor_url(), settings_navigation::TYPE_CUSTOM,
+            null, null, new pix_icon('icon', '', 'gradingform_guide'));
+    }
+
+    /**
+     * Extends the module navigation
+     *
+     * This function is called when the context for the page is an activity module with the
+     * FEATURE_ADVANCED_GRADING and there is an area with the active grading method set to the given plugin.
+     *
+     * @param global_navigation $navigation {@link global_navigation}
+     * @param navigation_node $node {@link navigation_node}
+     * @return void
+     */
+    public function extend_navigation(global_navigation $navigation, navigation_node $node=null) {
+        if (has_capability('moodle/grade:managegradingforms', $this->get_context())) {
+            // No need for preview if user can manage forms, he will have link to manage.php in settings instead.
+            return;
+        }
+        if ($this->is_form_defined() && ($options = $this->get_options()) && !empty($options['alwaysshowdefinition'])) {
+            $node->add(get_string('gradingof', 'gradingform_guide', get_grading_manager($this->get_areaid())->get_area_title()),
+                    new moodle_url('/grade/grading/form/'.$this->get_method_name().'/preview.php',
+                        array('areaid' => $this->get_areaid())), settings_navigation::TYPE_CUSTOM);
+        }
+    }
+
+    /**
+     * Saves the guide definition into the database
+     *
+     * @see parent::update_definition()
+     * @param stdClass $newdefinition guide definition data as coming from gradingform_guide_editguide::get_data()
+     * @param int $usermodified optional userid of the author of the definition, defaults to the current user
+     */
+    public function update_definition(stdClass $newdefinition, $usermodified = null) {
+        $this->update_or_check_guide($newdefinition, $usermodified, true);
+        if (isset($newdefinition->guide['regrade']) && $newdefinition->guide['regrade']) {
+            $this->mark_for_regrade();
+        }
+    }
+
+    /**
+     * Either saves the guide definition into the database or check if it has been changed.
+     *
+     * Returns the level of changes:
+     * 0 - no changes
+     * 1 - only texts or criteria sortorders are changed, students probably do not require re-grading
+     * 2 - added levels but maximum score on guide is the same, students still may not require re-grading
+     * 3 - removed criteria or changed number of points, students require re-grading but may be re-graded automatically
+     * 4 - removed levels - students require re-grading and not all students may be re-graded automatically
+     * 5 - added criteria - all students require manual re-grading
+     *
+     * @param stdClass $newdefinition guide definition data as coming from gradingform_guide_editguide::get_data()
+     * @param int|null $usermodified optional userid of the author of the definition, defaults to the current user
+     * @param bool $doupdate if true actually updates DB, otherwise performs a check
+     * @return int
+     */
+    public function update_or_check_guide(stdClass $newdefinition, $usermodified = null, $doupdate = false) {
+        global $DB;
+
+        // Firstly update the common definition data in the {grading_definition} table.
+        if ($this->definition === false) {
+            if (!$doupdate) {
+                // If we create the new definition there is no such thing as re-grading anyway.
+                return 5;
+            }
+            // If definition does not exist yet, create a blank one
+            // (we need id to save files embedded in description).
+            parent::update_definition(new stdClass(), $usermodified);
+            parent::load_definition();
+        }
+        if (!isset($newdefinition->guide['options'])) {
+            $newdefinition->guide['options'] = self::get_default_options();
+        }
+        $newdefinition->options = json_encode($newdefinition->guide['options']);
+        $editoroptions = self::description_form_field_options($this->get_context());
+        $newdefinition = file_postupdate_standard_editor($newdefinition, 'description', $editoroptions, $this->get_context(),
+            'grading', 'description', $this->definition->id);
+
+        // Reload the definition from the database.
+        $currentdefinition = $this->get_definition(true);
+
+        // Update guide data.
+        $haschanges = array();
+        if (empty($newdefinition->guide['criteria'])) {
+            $newcriteria = array();
+        } else {
+            $newcriteria = $newdefinition->guide['criteria']; // New ones to be saved.
+        }
+        $currentcriteria = $currentdefinition->guide_criteria;
+        $criteriafields = array('sortorder', 'description', 'descriptionformat', 'descriptionmarkers',
+            'descriptionmarkersformat', 'shortname', 'maxscore');
+        foreach ($newcriteria as $id => $criterion) {
+            if (preg_match('/^NEWID\d+$/', $id)) {
+                // Insert criterion into DB.
+                $data = array('definitionid' => $this->definition->id, 'descriptionformat' => FORMAT_MOODLE,
+                    'descriptionmarkersformat' => FORMAT_MOODLE); // TODO format is not supported yet.
+                foreach ($criteriafields as $key) {
+                    if (array_key_exists($key, $criterion)) {
+                        $data[$key] = $criterion[$key];
+                    }
+                }
+                if ($doupdate) {
+                    $id = $DB->insert_record('gradingform_guide_criteria', $data);
+                }
+                $haschanges[5] = true;
+            } else {
+                // Update criterion in DB.
+                $data = array();
+                foreach ($criteriafields as $key) {
+                    if (array_key_exists($key, $criterion) && $criterion[$key] != $currentcriteria[$id][$key]) {
+                        $data[$key] = $criterion[$key];
+                    }
+                }
+                if (!empty($data)) {
+                    // Update only if something is changed.
+                    $data['id'] = $id;
+                    if ($doupdate) {
+                        $DB->update_record('gradingform_guide_criteria', $data);
+                    }
+                    $haschanges[1] = true;
+                }
+            }
+        }
+        // Remove deleted criteria from DB.
+        foreach (array_keys($currentcriteria) as $id) {
+            if (!array_key_exists($id, $newcriteria)) {
+                if ($doupdate) {
+                    $DB->delete_records('gradingform_guide_criteria', array('id' => $id));
+                }
+                $haschanges[3] = true;
+            }
+        }
+        // Now handle comments.
+        if (empty($newdefinition->guide['comments'])) {
+            $newcomment = array();
+        } else {
+            $newcomment = $newdefinition->guide['comments']; // New ones to be saved.
+        }
+        $currentcomments = $currentdefinition->guide_comment;
+        $commentfields = array('sortorder', 'description');
+        foreach ($newcomment as $id => $comment) {
+            if (preg_match('/^NEWID\d+$/', $id)) {
+                // Insert criterion into DB.
+                $data = array('definitionid' => $this->definition->id, 'descriptionformat' => FORMAT_MOODLE);
+                foreach ($commentfields as $key) {
+                    if (array_key_exists($key, $comment)) {
+                        $data[$key] = $comment[$key];
+                    }
+                }
+                if ($doupdate) {
+                    $id = $DB->insert_record('gradingform_guide_comments', $data);
+                }
+            } else {
+                // Update criterion in DB.
+                $data = array();
+                foreach ($commentfields as $key) {
+                    if (array_key_exists($key, $comment) && $comment[$key] != $currentcomments[$id][$key]) {
+                        $data[$key] = $comment[$key];
+                    }
+                }
+                if (!empty($data)) {
+                    // Update only if something is changed.
+                    $data['id'] = $id;
+                    if ($doupdate) {
+                        $DB->update_record('gradingform_guide_comments', $data);
+                    }
+                }
+            }
+        }
+        // Remove deleted criteria from DB.
+        foreach (array_keys($currentcomments) as $id) {
+            if (!array_key_exists($id, $newcomment)) {
+                if ($doupdate) {
+                    $DB->delete_records('gradingform_guide_comments', array('id' => $id));
+                }
+            }
+        }
+        // End comments handle.
+        foreach (array('status', 'description', 'descriptionformat', 'name', 'options') as $key) {
+            if (isset($newdefinition->$key) && $newdefinition->$key != $this->definition->$key) {
+                $haschanges[1] = true;
+            }
+        }
+        if ($usermodified && $usermodified != $this->definition->usermodified) {
+            $haschanges[1] = true;
+        }
+        if (!count($haschanges)) {
+            return 0;
+        }
+        if ($doupdate) {
+            parent::update_definition($newdefinition, $usermodified);
+            $this->load_definition();
+        }
+        // Return the maximum level of changes.
+        $changelevels = array_keys($haschanges);
+        sort($changelevels);
+        return array_pop($changelevels);
+    }
+
+    /**
+     * Marks all instances filled with this guide with the status INSTANCE_STATUS_NEEDUPDATE
+     */
+    public function mark_for_regrade() {
+        global $DB;
+        if ($this->has_active_instances()) {
+            $conditions = array('definitionid'  => $this->definition->id,
+                        'status'  => gradingform_instance::INSTANCE_STATUS_ACTIVE);
+            $DB->set_field('grading_instances', 'status', gradingform_instance::INSTANCE_STATUS_NEEDUPDATE, $conditions);
+        }
+    }
+
+    /**
+     * Loads the guide form definition if it exists
+     *
+     * There is a new array called 'guide_criteria' appended to the list of parent's definition properties.
+     */
+    protected function load_definition() {
+        global $DB;
+
+        // Check to see if the user prefs have changed - putting here as this function is called on post even when
+        // validation on the page fails. - hard to find a better place to locate this as it is specific to the guide.
+        $showdesc = optional_param('showmarkerdesc', null, PARAM_BOOL); // Check if we need to change pref.
+        $showdescstudent = optional_param('showstudentdesc', null, PARAM_BOOL); // Check if we need to change pref.
+        if ($showdesc !== null) {
+            set_user_preference('gradingform_guide-showmarkerdesc', $showdesc);
+        }
+        if ($showdescstudent !== null) {
+            set_user_preference('gradingform_guide-showstudentdesc', $showdescstudent);
+        }
+
+        // Get definition.
+        $definition = $DB->get_record('grading_definitions', array('areaid' => $this->areaid,
+            'method' => $this->get_method_name()), '*');
+        if (!$definition) {
+            // The definition doesn't have to exist. It may be that we are only now creating it.
+            $this->definition = false;
+            return false;
+        }
+
+        $this->definition = $definition;
+        // Now get criteria.
+        $this->definition->guide_criteria = array();
+        $this->definition->guide_comment = array();
+        $criteria = $DB->get_recordset('gradingform_guide_criteria', array('definitionid' => $this->definition->id), 'sortorder');
+        foreach ($criteria as $criterion) {
+            foreach (array('id', 'sortorder', 'description', 'descriptionformat',
+                           'maxscore', 'descriptionmarkers', 'descriptionmarkersformat', 'shortname') as $fieldname) {
+                if ($fieldname == 'maxscore') {  // Strip any trailing 0.
+                    $this->definition->guide_criteria[$criterion->id][$fieldname] = (float)$criterion->{$fieldname};
+                } else {
+                    $this->definition->guide_criteria[$criterion->id][$fieldname] = $criterion->{$fieldname};
+                }
+            }
+        }
+        $criteria->close();
+
+        // Now get comments.
+        $comments = $DB->get_recordset('gradingform_guide_comments', array('definitionid' => $this->definition->id), 'sortorder');
+        foreach ($comments as $comment) {
+            foreach (array('id', 'sortorder', 'description', 'descriptionformat') as $fieldname) {
+                $this->definition->guide_comment[$comment->id][$fieldname] = $comment->{$fieldname};
+            }
+        }
+        $comments->close();
+
+        if (empty($this->moduleinstance)) { // Only set if empty.
+            $modulename = $this->get_component();
+            $context = $this->get_context();
+            if (strpos($modulename, 'mod_') === 0) {
+                $dbman = $DB->get_manager();
+                $modulename = substr($modulename, 4);
+                if ($dbman->table_exists($modulename)) {
+                    $this->moduleinstance = $DB->get_record($modulename, array('id' => $context->instanceid));
+                }
+            }
+        }
+    }
+
+    /**
+     * Returns the default options for the guide display
+     *
+     * @return array
+     */
+    public static function get_default_options() {
+        $options = array(
+            'alwaysshowdefinition' => 1,
+            'showmarkspercriterionstudents' => 1,
+        );
+        return $options;
+    }
+
+    /**
+     * Gets the options of this guide definition, fills the missing options with default values
+     *
+     * @return array
+     */
+    public function get_options() {
+        $options = self::get_default_options();
+        if (!empty($this->definition->options)) {
+            $thisoptions = json_decode($this->definition->options);
+            foreach ($thisoptions as $option => $value) {
+                $options[$option] = $value;
+            }
+        }
+        return $options;
+    }
+
+    /**
+     * Converts the current definition into an object suitable for the editor form's set_data()
+     *
+     * @param bool $addemptycriterion whether to add an empty criterion if the guide is completely empty (just being created)
+     * @return stdClass
+     */
+    public function get_definition_for_editing($addemptycriterion = false) {
+
+        $definition = $this->get_definition();
+        $properties = new stdClass();
+        $properties->areaid = $this->areaid;
+        if (isset($this->moduleinstance->grade)) {
+            $properties->modulegrade = $this->moduleinstance->grade;
+        }
+        if ($definition) {
+            foreach (array('id', 'name', 'description', 'descriptionformat', 'status') as $key) {
+                $properties->$key = $definition->$key;
+            }
+            $options = self::description_form_field_options($this->get_context());
+            $properties = file_prepare_standard_editor($properties, 'description', $options, $this->get_context(),
+                'grading', 'description', $definition->id);
+        }
+        $properties->guide = array('criteria' => array(), 'options' => $this->get_options(), 'comments' => array());
+        if (!empty($definition->guide_criteria)) {
+            $properties->guide['criteria'] = $definition->guide_criteria;
+        } else if (!$definition && $addemptycriterion) {
+            $properties->guide['criteria'] = array('addcriterion' => 1);
+        }
+        if (!empty($definition->guide_comment)) {
+            $properties->guide['comments'] = $definition->guide_comment;
+        } else if (!$definition && $addemptycriterion) {
+            $properties->guide['comments'] = array('addcomment' => 1);
+        }
+        return $properties;
+    }
+
+    /**
+     * Returns the form definition suitable for cloning into another area
+     *
+     * @see parent::get_definition_copy()
+     * @param gradingform_controller $target the controller of the new copy
+     * @return stdClass definition structure to pass to the target's {@link update_definition()}
+     */
+    public function get_definition_copy(gradingform_controller $target) {
+
+        $new = parent::get_definition_copy($target);
+        $old = $this->get_definition_for_editing();
+        $new->description_editor = $old->description_editor;
+        $new->guide = array('criteria' => array(), 'options' => $old->guide['options'], 'comments' => array());
+        $newcritid = 1;
+        foreach ($old->guide['criteria'] as $oldcritid => $oldcrit) {
+            unset($oldcrit['id']);
+            $new->guide['criteria']['NEWID'.$newcritid] = $oldcrit;
+            $newcritid++;
+        }
+        $newcomid = 1;
+        foreach ($old->guide['comments'] as $oldcritid => $oldcom) {
+            unset($oldcom['id']);
+            $new->guide['comments']['NEWID'.$newcomid] = $oldcom;
+            $newcomid++;
+        }
+        return $new;
+    }
+
+    /**
+     * Options for displaying the guide description field in the form
+     *
+     * @param context $context
+     * @return array options for the form description field
+     */
+    public static function description_form_field_options($context) {
+        global $CFG;
+        return array(
+            'maxfiles' => -1,
+            'maxbytes' => get_max_upload_file_size($CFG->maxbytes),
+            'context'  => $context,
+        );
+    }
+
+    /**
+     * Formats the definition description for display on page
+     *
+     * @return string
+     */
+    public function get_formatted_description() {
+        if (!isset($this->definition->description)) {
+            return '';
+        }
+        $context = $this->get_context();
+
+        $options = self::description_form_field_options($this->get_context());
+        $description = file_rewrite_pluginfile_urls($this->definition->description, 'pluginfile.php', $context->id,
+            'grading', 'description', $this->definition->id, $options);
+
+        $formatoptions = array(
+            'noclean' => false,
+            'trusted' => false,
+            'filter' => true,
+            'context' => $context
+        );
+        return format_text($description, $this->definition->descriptionformat, $formatoptions);
+    }
+
+    /**
+     * Returns the guide plugin renderer
+     *
+     * @param moodle_page $page the target page
+     * @return gradingform_guide_renderer
+     */
+    public function get_renderer(moodle_page $page) {
+        return $page->get_renderer('gradingform_'. $this->get_method_name());
+    }
+
+    /**
+     * Returns the HTML code displaying the preview of the grading form
+     *
+     * @param moodle_page $page the target page
+     * @return string
+     */
+    public function render_preview(moodle_page $page) {
+
+        if (!$this->is_form_defined()) {
+            throw new coding_exception('It is the caller\'s responsibility to make sure that the form is actually defined');
+        }
+
+        $output = $this->get_renderer($page);
+        $criteria = $this->definition->guide_criteria;
+        $comments = $this->definition->guide_comment;
+        $options = $this->get_options();
+        $guide = '';
+        if (has_capability('moodle/grade:managegradingforms', $page->context)) {
+            $guide .= $output->display_guide_mapping_explained($this->get_min_max_score());
+            $guide .= $output->display_guide($criteria, $comments, $options, self::DISPLAY_PREVIEW, 'guide');
+        } else {
+            $guide .= $output->display_guide($criteria, $comments, $options, self::DISPLAY_PREVIEW_GRADED, 'guide');
+        }
+
+        return $guide;
+    }
+
+    /**
+     * Deletes the guide definition and all the associated information
+     */
+    protected function delete_plugin_definition() {
+        global $DB;
+
+        // Get the list of instances.
+        $instances = array_keys($DB->get_records('grading_instances', array('definitionid' => $this->definition->id), '', 'id'));
+        // Delete all fillings.
+        $DB->delete_records_list('gradingform_guide_fillings', 'instanceid', $instances);
+        // Delete instances.
+        $DB->delete_records_list('grading_instances', 'id', $instances);
+        // Get the list of criteria records.
+        $criteria = array_keys($DB->get_records('gradingform_guide_criteria',
+            array('definitionid' => $this->definition->id), '', 'id'));
+        // Delete critera.
+        $DB->delete_records_list('gradingform_guide_criteria', 'id', $criteria);
+        // Delete comments.
+        $DB->delete_records('gradingform_guide_comments', array('definitionid' => $this->definition->id));
+    }
+
+    /**
+     * If instanceid is specified and grading instance exists and it is created by this rater for
+     * this item, this instance is returned.
+     * If there exists a draft for this raterid+itemid, take this draft (this is the change from parent)
+     * Otherwise new instance is created for the specified rater and itemid
+     *
+     * @param int $instanceid
+     * @param int $raterid
+     * @param int $itemid
+     * @return gradingform_instance
+     */
+    public function get_or_create_instance($instanceid, $raterid, $itemid) {
+        global $DB;
+        if ($instanceid &&
+                $instance = $DB->get_record('grading_instances',
+                    array('id'  => $instanceid, 'raterid' => $raterid, 'itemid' => $itemid), '*', IGNORE_MISSING)) {
+            return $this->get_instance($instance);
+        }
+        if ($itemid && $raterid) {
+            if ($rs = $DB->get_records('grading_instances', array('raterid' => $raterid, 'itemid' => $itemid),
+                'timemodified DESC', '*', 0, 1)) {
+                $record = reset($rs);
+                $currentinstance = $this->get_current_instance($raterid, $itemid);
+                if ($record->status == gradingform_guide_instance::INSTANCE_STATUS_INCOMPLETE &&
+                        (!$currentinstance || $record->timemodified > $currentinstance->get_data('timemodified'))) {
+                    $record->isrestored = true;
+                    return $this->get_instance($record);
+                }
+            }
+        }
+        return $this->create_instance($raterid, $itemid);
+    }
+
+    /**
+     * Returns html code to be included in student's feedback.
+     *
+     * @param moodle_page $page
+     * @param int $itemid
+     * @param array $gradinginfo result of function grade_get_grades
+     * @param string $defaultcontent default string to be returned if no active grading is found
+     * @param bool $cangrade whether current user has capability to grade in this context
+     * @return string
+     */
+    public function render_grade($page, $itemid, $gradinginfo, $defaultcontent, $cangrade) {
+        return $this->get_renderer($page)->display_instances($this->get_active_instances($itemid), $defaultcontent, $cangrade);
+    }
+
+    // Full-text search support.
+
+    /**
+     * Prepare the part of the search query to append to the FROM statement
+     *
+     * @param string $gdid the alias of grading_definitions.id column used by the caller
+     * @return string
+     */
+    public static function sql_search_from_tables($gdid) {
+        return " LEFT JOIN {gradingform_guide_criteria} gc ON (gc.definitionid = $gdid)";
+    }
+
+    /**
+     * Prepare the parts of the SQL WHERE statement to search for the given token
+     *
+     * The returned array cosists of the list of SQL comparions and the list of
+     * respective parameters for the comparisons. The returned chunks will be joined
+     * with other conditions using the OR operator.
+     *
+     * @param string $token token to search for
+     * @return array An array containing two more arrays
+     *     Array of search SQL fragments
+     *     Array of params for the search fragments
+     */
+    public static function sql_search_where($token) {
+        global $DB;
+
+        $subsql = array();
+        $params = array();
+
+        // Search in guide criteria description.
+        $subsql[] = $DB->sql_like('gc.description', '?', false, false);
+        $params[] = '%'.$DB->sql_like_escape($token).'%';
+
+        return array($subsql, $params);
+    }
+
+    /**
+     * Calculates and returns the possible minimum and maximum score (in points) for this guide
+     *
+     * @return array
+     */
+    public function get_min_max_score() {
+        if (!$this->is_form_available()) {
+            return null;
+        }
+        $returnvalue = array('minscore' => 0, 'maxscore' => 0);
+        $maxscore = 0;
+        foreach ($this->get_definition()->guide_criteria as $id => $criterion) {
+            $maxscore += $criterion['maxscore'];
+        }
+        $returnvalue['maxscore'] = $maxscore;
+        $returnvalue['minscore'] = 0;
+        if (!empty($this->moduleinstance->grade)) {
+            $returnvalue['modulegrade'] = $this->moduleinstance->grade;
+        }
+        return $returnvalue;
+    }
+}
+
+/**
+ * Class to manage one guide grading instance. Stores information and performs actions like
+ * update, copy, validate, submit, etc.
+ *
+ * @package    gradingform_guide
+ * @copyright  2012 Dan Marsden <dan@danmarsden.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class gradingform_guide_instance extends gradingform_instance {
+
+    /** @var array */
+    protected $guide;
+
+    /** @var array An array of validation errors */
+    protected $validationerrors = array();
+
+    /**
+     * Deletes this (INCOMPLETE) instance from database.
+     */
+    public function cancel() {
+        global $DB;
+        parent::cancel();
+        $DB->delete_records('gradingform_guide_fillings', array('instanceid' => $this->get_id()));
+    }
+
+    /**
+     * Duplicates the instance before editing (optionally substitutes raterid and/or itemid with
+     * the specified values)
+     *
+     * @param int $raterid value for raterid in the duplicate
+     * @param int $itemid value for itemid in the duplicate
+     * @return int id of the new instance
+     */
+    public function copy($raterid, $itemid) {
+        global $DB;
+        $instanceid = parent::copy($raterid, $itemid);
+        $currentgrade = $this->get_guide_filling();
+        foreach ($currentgrade['criteria'] as $criterionid => $record) {
+            $params = array('instanceid' => $instanceid, 'criterionid' => $criterionid,
+                'score' => $record['score'], 'remark' => $record['remark'], 'remarkformat' => $record['remarkformat']);
+            $DB->insert_record('gradingform_guide_fillings', $params);
+        }
+        return $instanceid;
+    }
+
+    /**
+     * Validates that guide is fully completed and contains valid grade on each criterion
+     *
+     * @param array $elementvalue value of element as came in form submit
+     * @return boolean true if the form data is validated and contains no errors
+     */
+    public function validate_grading_element($elementvalue) {
+        $criteria = $this->get_controller()->get_definition()->guide_criteria;
+        if (!isset($elementvalue['criteria']) || !is_array($elementvalue['criteria']) ||
+            count($elementvalue['criteria']) < count($criteria)) {
+            return false;
+        }
+        // Reset validation errors.
+        $this->validationerrors = null;
+        foreach ($criteria as $id => $criterion) {
+            if (!isset($elementvalue['criteria'][$id]['score'])
+                    || $criterion['maxscore'] < $elementvalue['criteria'][$id]['score']
+                    || !is_numeric($elementvalue['criteria'][$id]['score'])) {
+                $this->validationerrors[$id]['score'] =  $elementvalue['criteria'][$id]['score'];
+            }
+        }
+        if (!empty($this->validationerrors)) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Retrieves from DB and returns the data how this guide was filled
+     *
+     * @param bool $force whether to force DB query even if the data is cached
+     * @return array
+     */
+    public function get_guide_filling($force = false) {
+        global $DB;
+        if ($this->guide === null || $force) {
+            $records = $DB->get_records('gradingform_guide_fillings', array('instanceid' => $this->get_id()));
+            $this->guide = array('criteria' => array());
+            foreach ($records as $record) {
+                $record->score = (float)$record->score; // Strip trailing 0.
+                $this->guide['criteria'][$record->criterionid] = (array)$record;
+            }
+        }
+        return $this->guide;
+    }
+
+    /**
+     * Updates the instance with the data received from grading form. This function may be
+     * called via AJAX when grading is not yet completed, so it does not change the
+     * status of the instance.
+     *
+     * @param array $data
+     */
+    public function update($data) {
+        global $DB;
+        $currentgrade = $this->get_guide_filling();
+        parent::update($data);
+
+        foreach ($data['criteria'] as $criterionid => $record) {
+            if (!array_key_exists($criterionid, $currentgrade['criteria'])) {
+                $newrecord = array('instanceid' => $this->get_id(), 'criterionid' => $criterionid,
+                    'score' => $record['score'], 'remarkformat' => FORMAT_MOODLE);
+                if (isset($record['remark'])) {
+                    $newrecord['remark'] = $record['remark'];
+                }
+                $DB->insert_record('gradingform_guide_fillings', $newrecord);
+            } else {
+                $newrecord = array('id' => $currentgrade['criteria'][$criterionid]['id']);
+                foreach (array('score', 'remark'/*, 'remarkformat' TODO */) as $key) {
+                    if (isset($record[$key]) && $currentgrade['criteria'][$criterionid][$key] != $record[$key]) {
+                        $newrecord[$key] = $record[$key];
+                    }
+                }
+                if (count($newrecord) > 1) {
+                    $DB->update_record('gradingform_guide_fillings', $newrecord);
+                }
+            }
+        }
+        foreach ($currentgrade['criteria'] as $criterionid => $record) {
+            if (!array_key_exists($criterionid, $data['criteria'])) {
+                $DB->delete_records('gradingform_guide_fillings', array('id' => $record['id']));
+            }
+        }
+        $this->get_guide_filling(true);
+    }
+
+    /**
+     * Calculates the grade to be pushed to the gradebook
+     *
+     * @return int the valid grade from $this->get_controller()->get_grade_range()
+     */
+    public function get_grade() {
+        global $DB, $USER;
+        $grade = $this->get_guide_filling();
+
+        if (!($scores = $this->get_controller()->get_min_max_score()) || $scores['maxscore'] <= $scores['minscore']) {
+            return -1;
+        }
+
+        $graderange = array_keys($this->get_controller()->get_grade_range());
+        if (empty($graderange)) {
+            return -1;
+        }
+        sort($graderange);
+        $mingrade = $graderange[0];
+        $maxgrade = $graderange[count($graderange) - 1];
+
+        $curscore = 0;
+        foreach ($grade['criteria'] as $record) {
+            $curscore += $record['score'];
+        }
+        return round(($curscore-$scores['minscore'])/($scores['maxscore']-$scores['minscore'])*
+            ($maxgrade-$mingrade), 0) + $mingrade;
+    }
+
+    /**
+     * Returns html for form element of type 'grading'.
+     *
+     * @param moodle_page $page
+     * @param MoodleQuickForm_grading $gradingformelement
+     * @return string
+     */
+    public function render_grading_element($page, $gradingformelement) {
+        if (!$gradingformelement->_flagFrozen) {
+            $module = array('name'=>'gradingform_guide', 'fullpath'=>'/grade/grading/form/guide/js/guide.js');
+            $page->requires->js_init_call('M.gradingform_guide.init', array(
+                array('name' => $gradingformelement->getName())), true, $module);
+            $mode = gradingform_guide_controller::DISPLAY_EVAL;
+        } else {
+            if ($gradingformelement->_persistantFreeze) {
+                $mode = gradingform_guide_controller::DISPLAY_EVAL_FROZEN;
+            } else {
+                $mode = gradingform_guide_controller::DISPLAY_REVIEW;
+            }
+        }
+        $criteria = $this->get_controller()->get_definition()->guide_criteria;
+        $comments = $this->get_controller()->get_definition()->guide_comment;
+        $options = $this->get_controller()->get_options();
+        $value = $gradingformelement->getValue();
+        $html = '';
+        if ($value === null) {
+            $value = $this->get_guide_filling();
+        } else if (!$this->validate_grading_element($value)) {
+            $html .= html_writer::tag('div', get_string('guidenotcompleted', 'gradingform_guide'),
+                array('class' => 'gradingform_guide-error'));
+            if (!empty($this->validationerrors)) {
+                foreach ($this->validationerrors as $id => $err) {
+                    $a = new stdClass();
+                    $a->criterianame = $criteria[$id]['shortname'];
+                    $a->maxscore = $criteria[$id]['maxscore'];
+                    $html .= html_writer::tag('div', get_string('err_scoreinvalid', 'gradingform_guide', $a),
+                        array('class' => 'gradingform_guide-error'));
+                }
+            }
+        }
+        $currentinstance = $this->get_current_instance();
+        if ($currentinstance && $currentinstance->get_status() == gradingform_instance::INSTANCE_STATUS_NEEDUPDATE) {
+            $html .= html_writer::tag('div', get_string('needregrademessage', 'gradingform_guide'),
+                array('class' => 'gradingform_guide-regrade'));
+        }
+        $haschanges = false;
+        if ($currentinstance) {
+            $curfilling = $currentinstance->get_guide_filling();
+            foreach ($curfilling['criteria'] as $criterionid => $curvalues) {
+                $value['criteria'][$criterionid]['score'] = $curvalues['score'];
+                $newremark = null;
+                $newscore = null;
+                if (isset($value['criteria'][$criterionid]['remark'])) {
+                    $newremark = $value['criteria'][$criterionid]['remark'];
+                }
+                if (isset($value['criteria'][$criterionid]['score'])) {
+                    $newscore = $value['criteria'][$criterionid]['score'];
+                }
+                if ($newscore != $curvalues['score'] || $newremark != $curvalues['remark']) {
+                    $haschanges = true;
+                }
+            }
+        }
+        if ($this->get_data('isrestored') && $haschanges) {
+            $html .= html_writer::tag('div', get_string('restoredfromdraft', 'gradingform_guide'),
+                array('class' => 'gradingform_guide-restored'));
+        }
+        if (!empty($options['showdescriptionteacher'])) {
+            $html .= html_writer::tag('div', $this->get_controller()->get_formatted_description(),
+                array('class' => 'gradingform_guide-description'));
+        }
+        $html .= $this->get_controller()->get_renderer($page)->display_guide($criteria, $comments, $options, $mode,
+            $gradingformelement->getName(), $value, $this->validationerrors);
+        return $html;
+    }
+}
\ No newline at end of file
diff --git a/grade/grading/form/guide/pix/icon.png b/grade/grading/form/guide/pix/icon.png
new file mode 100644 (file)
index 0000000..f1af82f
Binary files /dev/null and b/grade/grading/form/guide/pix/icon.png differ
diff --git a/grade/grading/form/guide/preview.php b/grade/grading/form/guide/preview.php
new file mode 100644 (file)
index 0000000..ba8aa5d
--- /dev/null
@@ -0,0 +1,56 @@
+<?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/>.
+
+/**
+ * Preview marking guide page
+ *
+ * @package    gradingform_guide
+ * @copyright  2012 Dan Marsden <dan@danmarsden.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+require_once(dirname(dirname(dirname(dirname(dirname(__FILE__))))).'/config.php');
+require_once(dirname(__FILE__).'/lib.php');
+require_once(dirname(__FILE__).'/edit_form.php');
+require_once($CFG->dirroot.'/grade/grading/lib.php');
+
+$areaid = required_param('areaid', PARAM_INT);
+
+$manager = get_grading_manager($areaid);
+
+list($context, $course, $cm) = get_context_info_array($manager->get_context()->id);
+
+require_login($course, true, $cm);
+
+$controller = $manager->get_controller('guide');
+$options = $controller->get_options();
+
+if (!$controller->is_form_defined() || empty($options['alwaysshowdefinition'])) {
+    throw new moodle_exception('nopermissions', 'error', '', get_string('previewmarkingguide', 'gradingform_guide'));
+}
+
+$title = get_string('gradingof', 'gradingform_guide', $manager->get_area_title());
+$PAGE->set_url(new moodle_url('/grade/grading/form/guide/preview.php', array('areaid' => $areaid)));
+$PAGE->set_title($title);
+$PAGE->set_heading($title);
+
+echo $OUTPUT->header();
+echo $OUTPUT->heading($title);
+if (!empty($options['showdescriptionstudent'])) {
+    echo $OUTPUT->box($controller->get_formatted_description(), 'gradingform_guide-description');
+}
+echo $controller->render_preview($PAGE);
+echo $OUTPUT->footer();
diff --git a/grade/grading/form/guide/renderer.php b/grade/grading/form/guide/renderer.php
new file mode 100644 (file)
index 0000000..16acab0
--- /dev/null
@@ -0,0 +1,634 @@
+<?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/>.
+
+/**
+ * Contains the Guide grading form renderer in all of its glory
+ *
+ * @package    gradingform_guide
+ * @copyright  2012 Dan Marsden <dan@danmarsden.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Grading method plugin renderer
+ *
+ * @package    gradingform_guide
+ * @copyright  2012 Dan Marsden <dan@danmarsden.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class gradingform_guide_renderer extends plugin_renderer_base {
+
+    /**
+     * This function returns html code for displaying criterion. Depending on $mode it may be the
+     * code to edit guide, to preview the guide, to evaluate somebody or to review the evaluation.
+     *
+     * This function may be called from display_guide() to display the whole guide, or it can be
+     * called by itself to return a template used by JavaScript to add new empty criteria to the
+     * guide being designed.
+     * In this case it will use macros like {NAME}, {LEVELS}, {CRITERION-id}, etc.
+     *
+     * When overriding this function it is very important to remember that all elements of html
+     * form (in edit or evaluate mode) must have the name $elementname.
+     *
+     * Also JavaScript relies on the class names of elements and when developer changes them
+     * script might stop working.
+     *
+     * @param int $mode guide display mode, one of gradingform_guide_controller::DISPLAY_* {@link gradingform_guide_controller()}
+     * @param array $options An array of options.
+     *      showmarkspercriterionstudents (bool) If true adds the current score to the display
+     * @param string $elementname the name of the form element (in editor mode) or the prefix for div ids (in view mode)
+     * @param array $criterion criterion data
+     * @param array $value (only in view mode) teacher's feedback on this criterion
+     * @param array $validationerrors An array containing validation errors to be shown
+     * @return string
+     */
+    public function criterion_template($mode, $options, $elementname = '{NAME}', $criterion = null, $value = null,
+                                       $validationerrors = null) {
+        if ($criterion === null || !is_array($criterion) || !array_key_exists('id', $criterion)) {
+            $criterion = array('id' => '{CRITERION-id}',
+                               'description' => '{CRITERION-description}',
+                               'sortorder' => '{CRITERION-sortorder}',
+                               'class' => '{CRITERION-class}',
+                               'descriptionmarkers' => '{CRITERION-descriptionmarkers}',
+                               'shortname' => '{CRITERION-shortname}',
+                               'maxscore' => '{CRITERION-maxscore}');
+        } else {
+            foreach (array('sortorder', 'description', 'class', 'shortname', 'descriptionmarkers', 'maxscore') as $key) {
+                // Set missing array elements to empty strings to avoid warnings.
+                if (!array_key_exists($key, $criterion)) {
+                    $criterion[$key] = '';
+                }
+            }
+        }
+
+        $criteriontemplate = html_writer::start_tag('tr', array('class' => 'criterion'. $criterion['class'],
+            'id' => '{NAME}-criteria-{CRITERION-id}'));
+        $descriptionclass = 'description';
+        if ($mode == gradingform_guide_controller::DISPLAY_EDIT_FULL) {
+            $criteriontemplate .= html_writer::start_tag('td', array('class' => 'controls'));
+            foreach (array('moveup', 'delete', 'movedown') as $key) {
+                $value = get_string('criterion'.$key, 'gradingform_guide');
+                $button = html_writer::empty_tag('input', array('type' => 'submit',
+                    'name' => '{NAME}[criteria][{CRITERION-id}]['.$key.']',
+                    'id' => '{NAME}-criteria-{CRITERION-id}-'.$key, 'value' => $value, 'title' => $value, 'tabindex' => -1));
+                $criteriontemplate .= html_writer::tag('div', $button, array('class' => $key));
+            }
+            $criteriontemplate .= html_writer::end_tag('td'); // Controls.
+            $criteriontemplate .= html_writer::empty_tag('input', array('type' => 'hidden',
+                'name' => '{NAME}[criteria][{CRITERION-id}][sortorder]', 'value' => $criterion['sortorder']));
+
+            $shortname = html_writer::empty_tag('input', array('type'=> 'text',
+                'name' => '{NAME}[criteria][{CRITERION-id}][shortname]',  'value' => htmlspecialchars($criterion['shortname'])));
+            $shortname = html_writer::tag('div', $shortname, array('class'=>'criterionname'));
+            $description = html_writer::tag('textarea', htmlspecialchars($criterion['description']),
+                array('name' => '{NAME}[criteria][{CRITERION-id}][description]', 'cols' => '65', 'rows' => '5'));
+            $description = html_writer::tag('div', $description, array('class'=>'criteriondesc'));
+
+            $descriptionmarkers = html_writer::tag('textarea', htmlspecialchars($criterion['descriptionmarkers']),
+                array('name' => '{NAME}[criteria][{CRITERION-id}][descriptionmarkers]', 'cols' => '65', 'rows' => '5'));
+            $descriptionmarkers = html_writer::tag('div', $descriptionmarkers, array('class'=>'criteriondescmarkers'));
+
+            $maxscore = html_writer::empty_tag('input', array('type'=> 'text',
+                'name' => '{NAME}[criteria][{CRITERION-id}][maxscore]', 'size' => '3',
+                'value' => htmlspecialchars($criterion['maxscore'])));
+            $maxscore = html_writer::tag('div', $maxscore, array('class'=>'criterionmaxscore'));
+        } else {
+            if ($mode == gradingform_guide_controller::DISPLAY_EDIT_FROZEN) {
+                $criteriontemplate .= html_writer::empty_tag('input', array('type' => 'hidden',
+                    'name' => '{NAME}[criteria][{CRITERION-id}][sortorder]', 'value' => $criterion['sortorder']));
+                $criteriontemplate .= html_writer::empty_tag('input', array('type' => 'hidden',
+                    'name' => '{NAME}[criteria][{CRITERION-id}][shortname]', 'value' => $criterion['shortname']));
+                $criteriontemplate .= html_writer::empty_tag('input', array('type' => 'hidden',
+                    'name' => '{NAME}[criteria][{CRITERION-id}][description]', 'value' => $criterion['description']));
+                $criteriontemplate .= html_writer::empty_tag('input', array('type' => 'hidden',
+                    'name' => '{NAME}[criteria][{CRITERION-id}][descriptionmarkers]', 'value' => $criterion['descriptionmarkers']));
+                $criteriontemplate .= html_writer::empty_tag('input', array('type' => 'hidden',
+                    'name' => '{NAME}[criteria][{CRITERION-id}][maxscore]', 'value' => $criterion['maxscore']));
+            } else if ($mode == gradingform_guide_controller::DISPLAY_EVAL ||
+                       $mode == gradingform_guide_controller::DISPLAY_VIEW) {
+                $descriptionclass = 'descriptionreadonly';
+            }
+            $shortname   = html_writer::tag('div', $criterion['shortname'],
+                array('class'=>'criterionshortname', 'name' => '{NAME}[criteria][{CRITERION-id}][shortname]'));
+            $descmarkerclass = '';
+            $descstudentclass = '';
+            if ($mode == gradingform_guide_controller::DISPLAY_EVAL) {
+                if (!get_user_preferences('gradingform_guide-showmarkerdesc', true)) {
+                    $descmarkerclass = ' hide';
+                }
+                if (!get_user_preferences('gradingform_guide-showstudentdesc', true)) {
+                    $descstudentclass = ' hide';
+                }
+            }
+            $description = html_writer::tag('div', $criterion['description'],
+                array('class'=>'criteriondescription'.$descstudentclass,
+                      'name' => '{NAME}[criteria][{CRITERION-id}][descriptionmarkers]'));
+            $descriptionmarkers   = html_writer::tag('div', $criterion['descriptionmarkers'],
+                array('class'=>'criteriondescriptionmarkers'.$descmarkerclass,
+                      'name' => '{NAME}[criteria][{CRITERION-id}][descriptionmarkers]'));
+            $maxscore   = html_writer::tag('div', $criterion['maxscore'],
+                array('class'=>'criteriondescriptionscore', 'name' => '{NAME}[criteria][{CRITERION-id}][maxscore]'));
+        }
+
+        if (isset($criterion['error_description'])) {
+            $descriptionclass .= ' error';
+        }
+
+        $title = html_writer::tag('label', get_string('criterion', 'gradingform_guide'),
+            array('for'=>'{NAME}[criteria][{CRITERION-id}][shortname]', 'class' => 'criterionnamelabel'));
+        $title .= $shortname;
+        if ($mode == gradingform_guide_controller::DISPLAY_EDIT_FULL ||
+            $mode == gradingform_guide_controller::DISPLAY_PREVIEW) {
+            $title .= html_writer::tag('label', get_string('descriptionstudents', 'gradingform_guide'),
+                array('for'=>'{NAME}[criteria][{CRITERION-id}][description]'));
+            $title .= $description;
+            $title .= html_writer::tag('label', get_string('descriptionmarkers', 'gradingform_guide'),
+                array('for'=>'{NAME}[criteria][{CRITERION-id}][descriptionmarkers]'));
+            $title .= $descriptionmarkers;
+            $title .=  html_writer::tag('label', get_string('maxscore', 'gradingform_guide'),
+                array('for'=>'{NAME}[criteria][{CRITERION-id}][maxscore]'));
+            $title .= $maxscore;
+        } else if ($mode == gradingform_guide_controller::DISPLAY_PREVIEW_GRADED ||
+                   $mode == gradingform_guide_controller::DISPLAY_VIEW) {
+            $title .= $description;
+        } else {
+            $title .= $description . $descriptionmarkers;
+        }
+        $criteriontemplate .= html_writer::tag('td', $title, array('class' => $descriptionclass,
+            'id' => '{NAME}-criteria-{CRITERION-id}-shortname'));
+
+        $currentremark = '';
+        $currentscore = '';
+        if (isset($value['remark'])) {
+            $currentremark = $value['remark'];
+        }
+        if (isset($value['score'])) {
+            $currentscore = $value['score'];
+        }
+        if ($mode == gradingform_guide_controller::DISPLAY_EVAL) {
+            $scoreclass = '';
+            if (!empty($validationerrors[$criterion['id']]['score'])) {
+                $scoreclass = 'error';
+                $currentscore = $validationerrors[$criterion['id']]['score']; // Show invalid score in form.
+            }
+            $input = html_writer::tag('textarea', htmlspecialchars($currentremark),
+                array('name' => '{NAME}[criteria][{CRITERION-id}][remark]', 'cols' => '65', 'rows' => '5',
+                      'class' => 'markingguideremark'));
+            $criteriontemplate .= html_writer::tag('td', $input, array('class' => 'remark'));
+            $score = html_writer::tag('label', get_string('score', 'gradingform_guide'),
+                array('for'=>'{NAME}[criteria][{CRITERION-id}][score]', 'class' => $scoreclass));
+            $score .= html_writer::empty_tag('input', array('type'=> 'text',
+                'name' => '{NAME}[criteria][{CRITERION-id}][score]', 'class' => $scoreclass,
+                'size' => '3', 'value' => htmlspecialchars($currentscore)));
+            $score .= '/'.$maxscore;
+
+            $criteriontemplate .= html_writer::tag('td', $score, array('class' => 'score'));
+        } else if ($mode == gradingform_guide_controller::DISPLAY_EVAL_FROZEN) {
+            $criteriontemplate .= html_writer::empty_tag('input', array('type' => 'hidden',
+                'name' => '{NAME}[criteria][{CRITERION-id}][remark]', 'value' => $currentremark));
+        } else if ($mode == gradingform_guide_controller::DISPLAY_REVIEW ||
+            $mode == gradingform_guide_controller::DISPLAY_VIEW) {
+            $criteriontemplate .= html_writer::tag('td', $currentremark, array('class' => 'remark'));
+            if (!empty($options['showmarkspercriterionstudents'])) {
+                $criteriontemplate .= html_writer::tag('td', htmlspecialchars($currentscore). ' / '.$maxscore,
+                    array('class' => 'score'));
+            }
+        }
+        $criteriontemplate .= html_writer::end_tag('tr'); // Criterion.
+
+        $criteriontemplate = str_replace('{NAME}', $elementname, $criteriontemplate);
+        $criteriontemplate = str_replace('{CRITERION-id}', $criterion['id'], $criteriontemplate);
+        return $criteriontemplate;
+    }
+
+    /**
+     * This function returns html code for displaying criterion. Depending on $mode it may be the
+     * code to edit guide, to preview the guide, to evaluate somebody or to review the evaluation.
+     *
+     * This function may be called from display_guide() to display the whole guide, or it can be
+     * called by itself to return a template used by JavaScript to add new empty criteria to the
+     * guide being designed.
+     * In this case it will use macros like {NAME}, {LEVELS}, {CRITERION-id}, etc.
+     *
+     * When overriding this function it is very important to remember that all elements of html
+     * form (in edit or evaluate mode) must have the name $elementname.
+     *
+     * Also JavaScript relies on the class names of elements and when developer changes them
+     * script might stop working.
+     *
+     * @param int $mode guide display mode, one of gradingform_guide_controller::DISPLAY_* {@link gradingform_guide_controller}
+     * @param string $elementname the name of the form element (in editor mode) or the prefix for div ids (in view mode)
+     * @param array $comment
+     * @return string
+     */
+    public function comment_template($mode, $elementname = '{NAME}', $comment = null) {
+        if ($comment === null || !is_array($comment) || !array_key_exists('id', $comment)) {
+            $comment = array('id' => '{COMMENT-id}',
+                'description' => '{COMMENT-description}',
+                'sortorder' => '{COMMENT-sortorder}',
+                'class' => '{COMMENT-class}');
+        } else {
+            foreach (array('sortorder', 'description', 'class') as $key) {
+                // Set missing array elements to empty strings to avoid warnings.
+                if (!array_key_exists($key, $comment)) {
+                    $criterion[$key] = '';
+                }
+            }
+        }
+        $criteriontemplate = html_writer::start_tag('tr', array('class' => 'criterion'. $comment['class'],
+            'id' => '{NAME}-comments-{COMMENT-id}'));
+        if ($mode == gradingform_guide_controller::DISPLAY_EDIT_FULL) {
+            $criteriontemplate .= html_writer::start_tag('td', array('class' => 'controls'));
+            foreach (array('moveup', 'delete', 'movedown') as $key) {
+                $value = get_string('comments'.$key, 'gradingform_guide');
+                $button = html_writer::empty_tag('input', array('type' => 'submit',
+                    'name' => '{NAME}[comments][{COMMENT-id}]['.$key.']', 'id' => '{NAME}-comments-{COMMENT-id}-'.$key,
+                    'value' => $value, 'title' => $value, 'tabindex' => -1));
+                $criteriontemplate .= html_writer::tag('div', $button, array('class' => $key));
+            }
+            $criteriontemplate .= html_writer::end_tag('td'); // Controls.
+            $criteriontemplate .= html_writer::empty_tag('input', array('type' => 'hidden',
+                'name' => '{NAME}[comments][{COMMENT-id}][sortorder]', 'value' => $comment['sortorder']));
+            $description = html_writer::tag('textarea', htmlspecialchars($comment['description']),
+                array('name' => '{NAME}[comments][{COMMENT-id}][description]', 'cols' => '65', 'rows' => '5'));
+            $description = html_writer::tag('div', $description, array('class'=>'criteriondesc'));
+        } else {
+            if ($mode == gradingform_guide_controller::DISPLAY_EDIT_FROZEN) {
+                $criteriontemplate .= html_writer::empty_tag('input', array('type' => 'hidden',
+                    'name' => '{NAME}[comments][{COMMENT-id}][sortorder]', 'value' => $comment['sortorder']));
+                $criteriontemplate .= html_writer::empty_tag('input', array('type' => 'hidden',
+                    'name' => '{NAME}[comments][{COMMENT-id}][description]', 'value' => $comment['description']));
+            }
+            if ($mode == gradingform_guide_controller::DISPLAY_EVAL) {
+                $description = html_writer::tag('span', htmlspecialchars($comment['description']),
+                    array('name' => '{NAME}[comments][{COMMENT-id}][description]',
+                          'title' => get_string('clicktocopy', 'gradingform_guide'),
+                          'id' => '{NAME}[comments][{COMMENT-id}]', 'class'=>'markingguidecomment'));
+            } else {
+                $description = $comment['description'];
+            }
+        }
+        $descriptionclass = 'description';
+        if (isset($comment['error_description'])) {
+            $descriptionclass .= ' error';
+        }
+        $criteriontemplate .= html_writer::tag('td', $description, array('class' => $descriptionclass,
+            'id' => '{NAME}-comments-{COMMENT-id}-description'));
+        $criteriontemplate .= html_writer::end_tag('tr'); // Criterion.
+
+        $criteriontemplate = str_replace('{NAME}', $elementname, $criteriontemplate);
+        $criteriontemplate = str_replace('{COMMENT-id}', $comment['id'], $criteriontemplate);
+        return $criteriontemplate;
+    }
+    /**
+     * This function returns html code for displaying guide template (content before and after
+     * criteria list). Depending on $mode it may be the code to edit guide, to preview the guide,
+     * to evaluate somebody or to review the evaluation.
+     *
+     * This function is called from display_guide() to display the whole guide.
+     *
+     * When overriding this function it is very important to remember that all elements of html
+     * form (in edit or evaluate mode) must have the name $elementname.
+     *
+     * Also JavaScript relies on the class names of elements and when developer changes them
+     * script might stop working.
+     *
+     * @param int $mode guide display mode, one of gradingform_guide_controller::DISPLAY_* {@link gradingform_guide_controller}
+     * @param array $options An array of options provided to {@link gradingform_guide_renderer::guide_edit_options()}
+     * @param string $elementname the name of the form element (in editor mode) or the prefix for div ids (in view mode)
+     * @param string $criteriastr evaluated templates for this guide's criteria
+     * @param string $commentstr
+     * @return string
+     */
+    protected function guide_template($mode, $options, $elementname, $criteriastr, $commentstr) {
+        $classsuffix = ''; // CSS suffix for class of the main div. Depends on the mode.
+        switch ($mode) {
+            case gradingform_guide_controller::DISPLAY_EDIT_FULL:
+                $classsuffix = ' editor editable';
+                break;
+            case gradingform_guide_controller::DISPLAY_EDIT_FROZEN:
+                $classsuffix = ' editor frozen';
+                break;
+            case gradingform_guide_controller::DISPLAY_PREVIEW:
+            case gradingform_guide_controller::DISPLAY_PREVIEW_GRADED:
+                $classsuffix = ' editor preview';
+                break;
+            case gradingform_guide_controller::DISPLAY_EVAL:
+                $classsuffix = ' evaluate editable';
+                break;
+            case gradingform_guide_controller::DISPLAY_EVAL_FROZEN:
+                $classsuffix = ' evaluate frozen';
+                break;
+            case gradingform_guide_controller::DISPLAY_REVIEW:
+                $classsuffix = ' review';
+                break;
+            case gradingform_guide_controller::DISPLAY_VIEW:
+                $classsuffix = ' view';
+                break;
+        }
+
+        $guidetemplate = html_writer::start_tag('div', array('id' => 'guide-{NAME}',
+            'class' => 'clearfix gradingform_guide'.$classsuffix));
+        $guidetemplate .= html_writer::tag('table', $criteriastr, array('class' => 'criteria', 'id' => '{NAME}-criteria'));
+        if ($mode == gradingform_guide_controller::DISPLAY_EDIT_FULL) {
+            $value = get_string('addcriterion', 'gradingform_guide');
+            $input = html_writer::empty_tag('input', array('type' => 'submit', 'name' => '{NAME}[criteria][addcriterion]',
+                'id' => '{NAME}-criteria-addcriterion', 'value' => $value, 'title' => $value));
+            $guidetemplate .= html_writer::tag('div', $input, array('class' => 'addcriterion'));
+        }
+
+        if (!empty($commentstr)) {
+            $guidetemplate .= html_writer::tag('label', get_string('comments', 'gradingform_guide'),
+                array('for' => '{NAME}-comments', 'class' => 'commentheader'));
+            $guidetemplate .= html_writer::tag('table', $commentstr, array('class' => 'comments', 'id' => '{NAME}-comments'));
+        }
+        if ($mode == gradingform_guide_controller::DISPLAY_EDIT_FULL) {
+            $value = get_string('addcomment', 'gradingform_guide');
+            $input = html_writer::empty_tag('input', array('type' => 'submit', 'name' => '{NAME}[comments][addcomment]',
+                'id' => '{NAME}-comments-addcomment', 'value' => $value, 'title' => $value));
+            $guidetemplate .= html_writer::tag('div', $input, array('class' => 'addcomment'));
+        }
+
+        $guidetemplate .= $this->guide_edit_options($mode, $options);
+        $guidetemplate .= html_writer::end_tag('div');
+
+        return str_replace('{NAME}', $elementname, $guidetemplate);
+    }
+
+    /**
+     * Generates html template to view/edit the guide options. Expression {NAME} is used in
+     * template for the form element name
+     *
+     * @param int $mode guide display mode, one of gradingform_guide_controller::DISPLAY_* {@link gradingform_guide_controller}
+     * @param array $options
+     * @return string
+     */
+    protected function guide_edit_options($mode, $options) {
+        if ($mode != gradingform_guide_controller::DISPLAY_EDIT_FULL
+            && $mode != gradingform_guide_controller::DISPLAY_EDIT_FROZEN
+            && $mode != gradingform_guide_controller::DISPLAY_PREVIEW) {
+            // Options are displayed only for people who can manage.
+            return;
+        }
+        $html = html_writer::start_tag('div', array('class' => 'options'));
+        $html .= html_writer::tag('div', get_string('guideoptions', 'gradingform_guide'), array('class' => 'optionsheading'));
+        $attrs = array('type' => 'hidden', 'name' => '{NAME}[options][optionsset]', 'value' => 1);
+        foreach ($options as $option => $value) {
+            $html .= html_writer::start_tag('div', array('class' => 'option '.$option));
+            $attrs = array('name' => '{NAME}[options]['.$option.']', 'id' => '{NAME}-options-'.$option);
+            switch ($option) {
+                case 'sortlevelsasc':
+                    // Display option as dropdown.
+                    $html .= html_writer::tag('span', get_string($option, 'gradingform_guide'), array('class' => 'label'));
+                    $value = (int)(!!$value); // Make sure $value is either 0 or 1.
+                    if ($mode == gradingform_guide_controller::DISPLAY_EDIT_FULL) {
+                        $selectoptions = array(0 => get_string($option.'0', 'gradingform_guide'),
+                            1 => get_string($option.'1', 'gradingform_guide'));
+                        $valuestr = html_writer::select($selectoptions, $attrs['name'], $value, false, array('id' => $attrs['id']));
+                        $html .= html_writer::tag('span', $valuestr, array('class' => 'value'));
+                        // TODO add here button 'Sort levels'.
+                    } else {
+                        $html .= html_writer::tag('span', get_string($option.$value, 'gradingform_guide'),
+                            array('class' => 'value'));
+                        if ($mode == gradingform_guide_controller::DISPLAY_EDIT_FROZEN) {
+                            $html .= html_writer::empty_tag('input', $attrs + array('type' => 'hidden', 'value' => $value));
+                        }
+                    }
+                    break;
+                default:
+                    if ($mode == gradingform_guide_controller::DISPLAY_EDIT_FROZEN && $value) {
+                        $html .= html_writer::empty_tag('input', $attrs + array('type' => 'hidden', 'value' => $value));
+                    }
+                    // Display option as checkbox.
+                    $attrs['type'] = 'checkbox';
+                    $attrs['value'] = 1;
+                    if ($value) {
+                        $attrs['checked'] = 'checked';
+                    }
+                    if ($mode == gradingform_guide_controller::DISPLAY_EDIT_FROZEN ||
+                        $mode == gradingform_guide_controller::DISPLAY_PREVIEW) {
+                        $attrs['disabled'] = 'disabled';
+                        unset($attrs['name']);
+                    }
+                    $html .= html_writer::empty_tag('input', $attrs);
+                    $html .= html_writer::tag('label', get_string($option, 'gradingform_guide'), array('for' => $attrs['id']));
+                    break;
+            }
+            $html .= html_writer::end_tag('div'); // Option.
+        }
+        $html .= html_writer::end_tag('div'); // Options.
+        return $html;
+    }
+
+    /**
+     * This function returns html code for displaying guide. Depending on $mode it may be the code
+     * to edit guide, to preview the guide, to evaluate somebody or to review the evaluation.
+     *
+     * It is very unlikely that this function needs to be overriden by theme. It does not produce
+     * any html code, it just prepares data about guide design and evaluation, adds the CSS
+     * class to elements and calls the functions level_template, criterion_template and
+     * guide_template
+     *
+     * @param array $criteria data about the guide design
+     * @param array $comments
+     * @param array $options
+     * @param int $mode guide display mode, one of gradingform_guide_controller::DISPLAY_* {@link gradingform_guide_controller}
+     * @param string $elementname the name of the form element (in editor mode) or the prefix for div ids (in view mode)
+     * @param array $values evaluation result
+     * @param array $validationerrors
+     * @return string
+     */
+    public function display_guide($criteria, $comments, $options, $mode, $elementname = null, $values = null,
+                                  $validationerrors = null) {
+        $criteriastr = '';
+        $cnt = 0;
+        foreach ($criteria as $id => $criterion) {
+            $criterion['class'] = $this->get_css_class_suffix($cnt++, count($criteria) -1);
+            $criterion['id'] = $id;
+            if (isset($values['criteria'][$id])) {
+                $criterionvalue = $values['criteria'][$id];
+            } else {
+                $criterionvalue = null;
+            }
+            $criteriastr .= $this->criterion_template($mode, $options, $elementname, $criterion, $criterionvalue,
+                                                      $validationerrors);
+        }
+        $cnt = 0;
+        $commentstr = '';
+        // Check if comments should be displayed.
+        if ($mode == gradingform_guide_controller::DISPLAY_EDIT_FULL ||
+            $mode == gradingform_guide_controller::DISPLAY_EDIT_FROZEN ||
+            $mode == gradingform_guide_controller::DISPLAY_PREVIEW ||
+            $mode == gradingform_guide_controller::DISPLAY_EVAL ||
+            $mode == gradingform_guide_controller::DISPLAY_EVAL_FROZEN) {
+
+            foreach ($comments as $id => $comment) {
+                $comment['id'] = $id;
+                $comment['class'] = $this->get_css_class_suffix($cnt++, count($comments) -1);
+                $commentstr  .= $this->comment_template($mode, $elementname, $comment);
+            }
+        }
+        $output = $this->guide_template($mode, $options, $elementname, $criteriastr, $commentstr);
+        if ($mode == gradingform_guide_controller::DISPLAY_EVAL) {
+            $showdesc = get_user_preferences('gradingform_guide-showmarkerdesc', true);
+            $showdescstud = get_user_preferences('gradingform_guide-showstudentdesc', true);
+            $checked1 = array();
+            $checked2 = array();
+            $checked_s1 = array();
+            $checked_s2 = array();
+            $checked = array('checked' => 'checked');
+            if ($showdesc) {
+                $checked1 = $checked;
+            } else {
+                $checked2 = $checked;
+            }
+            if ($showdescstud) {
+                $checked_s1 = $checked;
+            } else {
+                $checked_s2 = $checked;
+            }
+
+            $radio = html_writer::tag('input', get_string('showmarkerdesc', 'gradingform_guide'), array('type' => 'radio',
+                'name' => 'showmarkerdesc',
+                'value' => "true")+$checked1);
+            $radio .= html_writer::tag('input', get_string('hidemarkerdesc', 'gradingform_guide'), array('type' => 'radio',
+                'name' => 'showmarkerdesc',
+                'value' => "false")+$checked2);
+            $output .= html_writer::tag('div', $radio, array('class' => 'showmarkerdesc'));
+
+            $radio = html_writer::tag('input', get_string('showstudentdesc', 'gradingform_guide'), array('type' => 'radio',
+                'name' => 'showstudentdesc',
+                'value' => "true")+$checked_s1);
+            $radio .= html_writer::tag('input', get_string('hidestudentdesc', 'gradingform_guide'), array('type' => 'radio',
+                'name' => 'showstudentdesc',
+                'value' => "false")+$checked_s2);
+            $output .= html_writer::tag('div', $radio, array('class' => 'showstudentdesc'));
+        }
+        return $output;
+    }
+
+    /**
+     * Help function to return CSS class names for element (first/last/even/odd) with leading space
+     *
+     * @param int $idx index of this element in the row/column
+     * @param int $maxidx maximum index of the element in the row/column
+     * @return string
+     */
+    protected function get_css_class_suffix($idx, $maxidx) {
+        $class = '';
+        if ($idx == 0) {
+            $class .= ' first';
+        }
+        if ($idx == $maxidx) {
+            $class .= ' last';
+        }
+        if ($idx % 2) {
+            $class .= ' odd';
+        } else {
+            $class .= ' even';
+        }
+        return $class;
+    }
+
+    /**
+     * Displays for the student the list of instances or default content if no instances found
+     *
+     * @param array $instances array of objects of type gradingform_guide_instance
+     * @param string $defaultcontent default string that would be displayed without advanced grading
+     * @param bool $cangrade whether current user has capability to grade in this context
+     * @return string
+     */
+    public function display_instances($instances, $defaultcontent, $cangrade) {
+        $return = '';
+        if (count($instances)) {
+            $return .= html_writer::start_tag('div', array('class' => 'advancedgrade'));
+            $idx = 0;
+            foreach ($instances as $instance) {
+                $return .= $this->display_instance($instance, $idx++, $cangrade);
+            }
+            $return .= html_writer::end_tag('div');
+        }
+        return $return. $defaultcontent;
+    }
+
+    /**
+     * Displays one grading instance
+     *
+     * @param gradingform_guide_instance $instance
+     * @param int $idx unique number of instance on page
+     *&