Merge branch 'MDL-29598_master' of git://github.com/dmonllao/moodle
authorEloy Lafuente (stronk7) <stronk7@moodle.org>
Tue, 28 Aug 2012 21:23:26 +0000 (23:23 +0200)
committerEloy Lafuente (stronk7) <stronk7@moodle.org>
Tue, 28 Aug 2012 21:23:26 +0000 (23:23 +0200)
169 files changed:
admin/roles/lib.php
admin/tool/assignmentupgrade/lang/en/tool_assignmentupgrade.php
admin/tool/assignmentupgrade/module.js
admin/tool/assignmentupgrade/renderer.php
admin/user/user_bulk_cohortadd.php
backup/moodle2/backup_stepslib.php
blocks/community/block_community.php
blocks/edit_form.php
blocks/html/block_html.php
blocks/html/edit_form.php
blocks/html/lang/en/block_html.php
blocks/html/lib.php
blocks/html/settings.php [new file with mode: 0644]
blocks/quiz_results/block_quiz_results.php
cohort/assign.php
cohort/edit.php
cohort/edit_form.php
cohort/index.php
cohort/lib.php
comment/lib.php
comment/locallib.php
course/lib.php
draftfile.php
enrol/ajax.php
enrol/category/locallib.php
enrol/cohort/addinstance_form.php
enrol/cohort/lib.php
enrol/cohort/locallib.php
enrol/externallib.php
enrol/locallib.php
enrol/manual/ajax.php
enrol/manual/bulkchangeforms.php
enrol/manual/db/access.php
enrol/manual/db/install.php
enrol/manual/edit.php
enrol/manual/edit_form.php
enrol/manual/editenrolment.php
enrol/manual/editenrolment_form.php
enrol/manual/externallib.php
enrol/manual/lib.php
enrol/manual/locallib.php
enrol/manual/manage.php
enrol/manual/settings.php
enrol/manual/unenrolself.php
enrol/manual/version.php
enrol/renderer.php
enrol/self/db/access.php
enrol/self/db/install.php
enrol/self/edit.php
enrol/self/edit_form.php
enrol/self/editenrolment.php
enrol/self/editenrolment_form.php
enrol/self/lang/en/enrol_self.php
enrol/self/lib.php
enrol/self/locallib.php
enrol/self/settings.php
enrol/self/unenrolself.php
enrol/self/version.php
enrol/upgrade.txt
files/externallib.php
files/renderer.php
grade/grading/form/guide/lib.php
grade/grading/form/guide/preview.php
grade/grading/form/rubric/lib.php
grade/grading/form/rubric/preview.php
grade/grading/lib.php
grade/grading/manage.php
grade/lib.php
lib/blocklib.php
lib/completionlib.php
lib/db/install.xml
lib/db/upgrade.php
lib/editor/tinymce/adminlib.php
lib/editor/tinymce/classes/plugin.php
lib/editor/tinymce/lang/en/editor_tinymce.php
lib/editor/tinymce/lib.php
lib/editor/tinymce/plugins/dragmath/lib.php
lib/editor/tinymce/plugins/moodleemoticon/lib.php
lib/editor/tinymce/plugins/moodleimage/lib.php
lib/editor/tinymce/plugins/moodlemedia/lib.php
lib/editor/tinymce/plugins/moodlenolink/lib.php
lib/editor/tinymce/plugins/spellchecker/config.php
lib/editor/tinymce/plugins/spellchecker/db/install.php [new file with mode: 0644]
lib/editor/tinymce/plugins/spellchecker/db/upgrade.php [new file with mode: 0644]
lib/editor/tinymce/plugins/spellchecker/db/upgradelib.php [new file with mode: 0644]
lib/editor/tinymce/plugins/spellchecker/lib.php
lib/editor/tinymce/plugins/spellchecker/settings.php [new file with mode: 0644]
lib/editor/tinymce/plugins/spellchecker/version.php
lib/editor/tinymce/settings.php
lib/editor/tinymce/subplugins.php
lib/editor/tinymce/tests/editor_test.php [new file with mode: 0644]
lib/editor/tinymce/upgrade.txt [new file with mode: 0644]
lib/externallib.php
lib/filebrowser/file_info_context_course.php
lib/filebrowser/file_info_context_coursecat.php
lib/filebrowser/file_info_context_module.php
lib/modinfolib.php
lib/navigationlib.php
lib/questionlib.php
lib/session-test.php [deleted file]
lib/setuplib.php
lib/tests/accesslib_test.php
lib/tests/completionlib_test.php
lib/tests/questionlib_test.php
lib/weblib.php
message/edit.php
mod/assign/gradingtable.php
mod/assign/locallib.php
mod/choice/backup/moodle2/restore_choice_stepslib.php
mod/data/lib.php
mod/feedback/lang/en/feedback.php
mod/forum/lib.php
mod/glossary/lib.php
mod/quiz/edit.php
mod/quiz/editlib.php
mod/quiz/report/upgrade.txt
mod/wiki/edit_form.php
question/addquestion.php
question/category.php
question/category_class.php
question/editlib.php
question/engine/questionusage.php
question/format.php
question/format/blackboard/format.php
question/format/blackboard_six/format.php
question/format/blackboard_six/formatbase.php [new file with mode: 0644]
question/format/blackboard_six/formatpool.php [new file with mode: 0644]
question/format/blackboard_six/formatqti.php [new file with mode: 0644]
question/format/blackboard_six/lang/en/qformat_blackboard_six.php
question/format/blackboard_six/tests/blackboardformatpool_test.php [new file with mode: 0644]
question/format/blackboard_six/tests/blackboardsixformatqti_test.php [new file with mode: 0644]
question/format/blackboard_six/tests/fixtures/sample_blackboard_pool.dat [new file with mode: 0644]
question/format/blackboard_six/tests/fixtures/sample_blackboard_qti.dat [new file with mode: 0644]
question/format/blackboard_six/version.php
question/format/examview/format.php
question/format/gift/format.php
question/format/gift/tests/fixtures/questions.gift.txt
question/format/gift/tests/giftformat_test.php
question/format/multianswer/format.php
question/format/multianswer/tests/fixtures/questions.multianswer.txt [new file with mode: 0644]
question/format/multianswer/tests/multianswerformat_test.php [new file with mode: 0644]
question/import.php
question/preview.php
question/question.php
question/type/calculated/datasetdefinitions_form.php
question/type/calculated/datasetitems_form.php
question/type/calculated/questiontype.php
question/type/edit_question_form.php
question/type/essay/db/upgrade.php
question/type/match/lang/en/qtype_match.php
question/type/multianswer/lang/en/qtype_multianswer.php
question/type/multianswer/questiontype.php
question/type/multichoice/lang/en/qtype_multichoice.php
question/type/numerical/question.php
question/type/numerical/tests/answer_test.php
question/type/questiontypebase.php
question/type/shortanswer/lang/en/qtype_shortanswer.php
report/security/locallib.php
repository/coursefiles/lib.php
repository/draftfiles_ajax.php
repository/lib.php
repository/local/lib.php
repository/manage_instances.php
repository/recent/lib.php
repository/repository_ajax.php
theme/formal_white/style/formal_white.css
user/index.php
version.php
webservice/lib.php

index 43eb7f5..df537f0 100644 (file)
@@ -933,7 +933,7 @@ class override_permissions_table_advanced extends capability_table_with_risks {
         global $DB;
 
     /// Get the capabilities from the parent context, so that can be shown in the interface.
-        $parentcontext = get_context_instance_by_id(get_parent_contextid($this->context));
+        $parentcontext = context::instance_by_id(get_parent_contextid($this->context));
         $this->parentpermissions = role_context_capabilities($this->roleid, $parentcontext);
     }
 
@@ -996,7 +996,7 @@ abstract class role_assign_user_selector_base extends user_selector_base {
         if (isset($options['context'])) {
             $this->context = $options['context'];
         } else {
-            $this->context = get_context_instance_by_id($options['contextid']);
+            $this->context = context::instance_by_id($options['contextid']);
         }
         $options['accesscontext'] = $this->context;
         parent::__construct($name, $options);
@@ -1230,7 +1230,7 @@ class existing_role_holders extends role_assign_user_selector_base {
     }
 
     protected function parent_con_group_name($search, $contextid) {
-        $context = get_context_instance_by_id($contextid);
+        $context = context::instance_by_id($contextid);
         $contextname = print_context_name($context, true, true);
         if ($search) {
             $a = new stdClass;
@@ -1477,7 +1477,7 @@ class role_allow_switch_page extends role_allow_role_page {
 function roles_get_potential_user_selector($context, $name, $options) {
         $blockinsidecourse = false;
         if ($context->contextlevel == CONTEXT_BLOCK) {
-            $parentcontext = get_context_instance_by_id(get_parent_contextid($context));
+            $parentcontext = context::instance_by_id(get_parent_contextid($context));
             $blockinsidecourse = in_array($parentcontext->contextlevel, array(CONTEXT_MODULE, CONTEXT_COURSE));
         }
 
index e9a61dc..9dc8c6e 100644 (file)
@@ -36,6 +36,7 @@ $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['noassignmentsselected'] = 'No assignments selected';
 $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.';
index edee839..6370e70 100644 (file)
@@ -56,7 +56,7 @@ M.tool_assignmentupgrade = {
             assignmentsinput = Y.one('input.selectedassignments');
             assignmentsinput.set('value', selectedassignments.join(','));
             if (selectedassignments.length == 0) {
-                alert(M.str.assign.noassignmentsselected);
+                alert(M.str.tool_assignmentupgrade.noassignmentsselected);
                 e.preventDefault();
             }
         });
index ddb8cfa..29192d7 100644 (file)
@@ -119,7 +119,7 @@ class tool_assignmentupgrade_renderer extends plugin_renderer_base {
         $output = '';
         $output .= $this->header();
         $this->page->requires->js_init_call('M.tool_assignmentupgrade.init_upgrade_table', array());
-
+        $this->page->requires->string_for_js('noassignmentsselected', 'tool_assignmentupgrade');
 
         $output .= $this->heading(get_string('notupgradedtitle', 'tool_assignmentupgrade'));
         $output .= $this->box(get_string('notupgradedintro', 'tool_assignmentupgrade'));
index 1e32564..be72d51 100644 (file)
@@ -45,7 +45,7 @@ foreach ($allcohorts as $c) {
         // external cohorts can not be modified
         continue;
     }
-    $context = get_context_instance_by_id($c->contextid);
+    $context = context::instance_by_id($c->contextid);
     if (!has_capability('moodle/cohort:assign', $context)) {
         continue;
     }
index b4399f2..6b17bc2 100644 (file)
@@ -522,9 +522,12 @@ class backup_enrolments_structure_step extends backup_structure_step {
         $enrol = new backup_nested_element('enrol', array('id'), array(
             'enrol', 'status', 'sortorder', 'name', 'enrolperiod', 'enrolstartdate',
             'enrolenddate', 'expirynotify', 'expirytreshold', 'notifyall',
-            'password', 'cost', 'currency', 'roleid', 'customint1', 'customint2', 'customint3',
-            'customint4', 'customchar1', 'customchar2', 'customdec1', 'customdec2',
-            'customtext1', 'customtext2', 'timecreated', 'timemodified'));
+            'password', 'cost', 'currency', 'roleid',
+            'customint1', 'customint2', 'customint3', 'customint4', 'customint5', 'customint6', 'customint7', 'customint8',
+            'customchar1', 'customchar2', 'customchar3',
+            'customdec1', 'customdec2',
+            'customtext1', 'customtext2', 'customtext3', 'customtext4',
+            'timecreated', 'timemodified'));
 
         $userenrolments = new backup_nested_element('user_enrolments');
 
index 35e3f2e..c211342 100644 (file)
@@ -43,7 +43,7 @@ class block_community extends block_list {
     function user_can_edit() {
         // Don't allow people to edit the block if they can't even use it
         if (!has_capability('moodle/community:add',
-                        get_context_instance_by_id($this->instance->parentcontextid))) {
+                        context::instance_by_id($this->instance->parentcontextid))) {
             return false;
         }
         return parent::user_can_edit();
@@ -52,7 +52,7 @@ class block_community extends block_list {
     function get_content() {
         global $CFG, $OUTPUT, $USER;
 
-        $coursecontext = get_context_instance_by_id($this->instance->parentcontextid);
+        $coursecontext = context::instance_by_id($this->instance->parentcontextid);
 
         if (!has_capability('moodle/community:add', $coursecontext)
                 or $this->content !== NULL) {
index 9380fa2..169c54d 100644 (file)
@@ -87,7 +87,7 @@ class block_edit_form extends moodleform {
 
         $regionoptions = $this->page->theme->get_all_block_regions();
 
-        $parentcontext = get_context_instance_by_id($this->block->instance->parentcontextid);
+        $parentcontext = context::instance_by_id($this->block->instance->parentcontextid);
         $mform->addElement('hidden', 'bui_parentcontextid', $parentcontext->id);
 
         $mform->addElement('static', 'bui_homecontext', get_string('createdat', 'block'), print_context_name($parentcontext));
index 39040ae..67b108f 100644 (file)
@@ -29,6 +29,10 @@ class block_html extends block_base {
         $this->title = get_string('pluginname', 'block_html');
     }
 
+    function has_config() {
+        return true;
+    }
+
     function applicable_formats() {
         return array('all' => true);
     }
@@ -104,7 +108,7 @@ class block_html extends block_base {
     function content_is_trusted() {
         global $SCRIPT;
 
-        if (!$context = get_context_instance_by_id($this->instance->parentcontextid)) {
+        if (!$context = context::instance_by_id($this->instance->parentcontextid, IGNORE_MISSING)) {
             return false;
         }
         //find out if this block is on the profile page
@@ -138,10 +142,14 @@ class block_html extends block_base {
      * @return array
      */
     function html_attributes() {
+        global $CFG;
+
         $attributes = parent::html_attributes();
 
-        if (!empty($this->config->classes)) {
-            $attributes['class'] .= ' '.$this->config->classes;
+        if (!empty($CFG->block_html_allowcssclasses)) {
+            if (!empty($this->config->classes)) {
+                $attributes['class'] .= ' '.$this->config->classes;
+            }
         }
 
         return $attributes;
index f544f98..edb5705 100644 (file)
@@ -31,6 +31,8 @@
  */
 class block_html_edit_form extends block_edit_form {
     protected function specific_definition($mform) {
+        global $CFG;
+
         // Fields for editing HTML block title and contents.
         $mform->addElement('header', 'configheader', get_string('blocksettings', 'block'));
 
@@ -42,9 +44,11 @@ class block_html_edit_form extends block_edit_form {
         $mform->addRule('config_text', null, 'required', null, 'client');
         $mform->setType('config_text', PARAM_RAW); // XSS is prevented when printing the block contents and serving files
 
-        $mform->addElement('text', 'config_classes', get_string('configclasses', 'block_html'));
-        $mform->setType('config_classes', PARAM_TEXT);
-        $mform->addHelpButton('config_classes', 'configclasses', 'block_html');
+        if (!empty($CFG->block_html_allowcssclasses)) {
+            $mform->addElement('text', 'config_classes', get_string('configclasses', 'block_html'));
+            $mform->setType('config_classes', PARAM_TEXT);
+            $mform->addHelpButton('config_classes', 'configclasses', 'block_html');
+        }
     }
 
     function set_data($defaults) {
index 6e82be8..d331059 100644 (file)
@@ -23,7 +23,9 @@
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
-$string['configclasses'] = 'Additional HTML classes';
+$string['allowadditionalcssclasses'] = 'Allow additional CSS classes';
+$string['configallowadditionalcssclasses'] = 'Adds a configuration option to HTML block instances allowing additional CSS classes to be set.';
+$string['configclasses'] = 'Additional CSS classes';
 $string['configclasses_help'] = 'The purpose of this configuration is to aid with theming by helping distinguish HTML blocks from each other. Any CSS classes entered here (space delimited) will be appended to the block\'s default classes.';
 $string['configcontent'] = 'Content';
 $string['configtitle'] = 'Block title';
index a2555c3..03d58ca 100644 (file)
@@ -53,7 +53,7 @@ function block_html_pluginfile($course, $birecord_or_cm, $context, $filearea, $a
         send_file_not_found();
     }
 
-    if ($parentcontext = get_context_instance_by_id($birecord_or_cm->parentcontextid)) {
+    if ($parentcontext = context::instance_by_id($birecord_or_cm->parentcontextid, IGNORE_MISSING)) {
         if ($parentcontext->contextlevel == CONTEXT_USER) {
             // force download on all personal pages including /my/
             //because we do not have reliable way to find out from where this is used
diff --git a/blocks/html/settings.php b/blocks/html/settings.php
new file mode 100644 (file)
index 0000000..59cb797
--- /dev/null
@@ -0,0 +1,10 @@
+<?php
+
+defined('MOODLE_INTERNAL') || die;
+
+if ($ADMIN->fulltree) {
+    $settings->add(new admin_setting_configcheckbox('block_html_allowcssclasses', get_string('allowadditionalcssclasses', 'block_html'),
+                       get_string('configallowadditionalcssclasses', 'block_html'), 0));
+}
+
+
index d18af55..8795a68 100644 (file)
@@ -65,7 +65,7 @@ class block_quiz_results extends block_base {
         if (empty($this->instance->parentcontextid)) {
             return 0;
         }
-        $parentcontext = get_context_instance_by_id($this->instance->parentcontextid);
+        $parentcontext = context::instance_by_id($this->instance->parentcontextid);
         if ($parentcontext->contextlevel != CONTEXT_MODULE) {
             return 0;
         }
index b7f6589..5eaa2a7 100644 (file)
@@ -32,7 +32,7 @@ $id = required_param('id', PARAM_INT);
 require_login();
 
 $cohort = $DB->get_record('cohort', array('id'=>$id), '*', MUST_EXIST);
-$context = get_context_instance_by_id($cohort->contextid, MUST_EXIST);
+$context = context::instance_by_id($cohort->contextid, MUST_EXIST);
 
 require_capability('moodle/cohort:assign', $context);
 
index dc1bd08..721d790 100644 (file)
@@ -40,9 +40,9 @@ require_login();
 $category = null;
 if ($id) {
     $cohort = $DB->get_record('cohort', array('id'=>$id), '*', MUST_EXIST);
-    $context = get_context_instance_by_id($cohort->contextid, MUST_EXIST);
+    $context = context::instance_by_id($cohort->contextid, MUST_EXIST);
 } else {
-    $context = get_context_instance_by_id($contextid, MUST_EXIST);
+    $context = context::instance_by_id($contextid, MUST_EXIST);
     if ($context->contextlevel != CONTEXT_COURSECAT and $context->contextlevel != CONTEXT_SYSTEM) {
         print_error('invalidcontext');
     }
index 9576071..81e7a7c 100644 (file)
@@ -103,7 +103,7 @@ class cohort_edit_form extends moodleform {
         }
         // always add current - this is not likely, but if the logic gets changed it might be a problem
         if (!isset($options[$currentcontextid])) {
-            $context = get_context_instance_by_id($currentcontextid, MUST_EXIST);
+            $context = context::instance_by_id($currentcontextid, MUST_EXIST);
             $options[$context->id] = print_context_name($syscontext);
         }
         return $options;
index 026bcb8..00aab66 100644 (file)
@@ -35,7 +35,7 @@ $searchquery  = optional_param('search', '', PARAM_RAW);
 require_login();
 
 if ($contextid) {
-    $context = get_context_instance_by_id($contextid, MUST_EXIST);
+    $context = context::instance_by_id($contextid, MUST_EXIST);
 } else {
     $context = context_system::instance();
 }
index 223644b..67c2bf4 100644 (file)
@@ -155,6 +155,18 @@ function cohort_remove_member($cohortid, $userid) {
     events_trigger('cohort_member_removed', (object)array('cohortid'=>$cohortid, 'userid'=>$userid));
 }
 
+/**
+ * Is this user a cohort member?
+ * @param int $cohortid
+ * @param int $userid
+ * @return bool
+ */
+function cohort_is_member($cohortid, $userid) {
+    global $DB;
+
+    return $DB->record_exists('cohort_members', array('cohortid'=>$cohortid, 'userid'=>$userid));
+}
+
 /**
  * Returns list of visible cohorts in course.
  *
index 9bf2653..8954244 100644 (file)
@@ -120,7 +120,7 @@ class comment {
             $this->contextid = $this->context->id;
         } else if(!empty($options->contextid)) {
             $this->contextid = $options->contextid;
-            $this->context = get_context_instance_by_id($this->contextid);
+            $this->context = context::instance_by_id($this->contextid);
         } else {
             print_error('invalidcontext');
         }
index 5a8b0d7..c53fa80 100644 (file)
@@ -114,7 +114,7 @@ class comment_manager {
      */
     private function setup_plugin($comment) {
         global $DB;
-        $this->context = get_context_instance_by_id($comment->contextid);
+        $this->context = context::instance_by_id($comment->contextid, IGNORE_MISSING);
         if (!$this->context) {
             return false;
         }
index 75cc734..cf8f372 100644 (file)
@@ -531,10 +531,19 @@ function print_mnet_log($hostid, $course, $user=0, $date=0, $order="l.time ASC",
 
 function print_log_csv($course, $user, $date, $order='l.time DESC', $modname,
                         $modid, $modaction, $groupid) {
-    global $DB;
+    global $DB, $CFG;
+
+    require_once($CFG->libdir . '/csvlib.class.php');
 
-    $text = get_string('course')."\t".get_string('time')."\t".get_string('ip_address')."\t".
-            get_string('fullnameuser')."\t".get_string('action')."\t".get_string('info');
+    $csvexporter = new csv_export_writer('tab');
+
+    $header = array();
+    $header[] = get_string('course');
+    $header[] = get_string('time');
+    $header[] = get_string('ip_address');
+    $header[] = get_string('fullnameuser');
+    $header[] = get_string('action');
+    $header[] = get_string('info');
 
     if (!$logs = build_logs_array($course, $user, $date, $order, '', '',
                        $modname, $modid, $modaction, $groupid)) {
@@ -561,16 +570,10 @@ function print_log_csv($course, $user, $date, $order='l.time DESC', $modname,
 
     $strftimedatetime = get_string("strftimedatetime");
 
-    $filename = 'logs_'.userdate(time(),get_string('backupnameformat', 'langconfig'),99,false);
-    $filename .= '.txt';
-    header("Content-Type: application/download\n");
-    header("Content-Disposition: attachment; filename=\"$filename\"");
-    header("Expires: 0");
-    header("Cache-Control: must-revalidate,post-check=0,pre-check=0");
-    header("Pragma: public");
-
-    echo get_string('savedat').userdate(time(), $strftimedatetime)."\n";
-    echo $text."\n";
+    $csvexporter->set_filename('logs', '.txt');
+    $title = array(get_string('savedat').userdate(time(), $strftimedatetime));
+    $csvexporter->add_data($title);
+    $csvexporter->add_data($header);
 
     if (empty($logs['logs'])) {
         return true;
@@ -600,9 +603,9 @@ function print_log_csv($course, $user, $date, $order='l.time DESC', $modname,
         $firstField = format_string($courses[$log->course], true, array('context' => $coursecontext));
         $fullname = fullname($log, has_capability('moodle/site:viewfullnames', $coursecontext));
         $row = array($firstField, userdate($log->time, $strftimedatetime), $log->ip, $fullname, $log->module.' '.$log->action, $log->info);
-        $text = implode("\t", $row);
-        echo $text." \n";
+        $csvexporter->add_data($row);
     }
+    $csvexporter->download_file();
     return true;
 }
 
index ede5706..821ae57 100644 (file)
@@ -61,7 +61,7 @@ if ($component !== 'user' or $filearea !== 'draft') {
     send_file_not_found();
 }
 
-$context = get_context_instance_by_id($contextid);
+$context = context::instance_by_id($contextid);
 if ($context->contextlevel != CONTEXT_USER) {
     send_file_not_found();
 }
index 67ae497..ede3c5a 100644 (file)
@@ -63,7 +63,7 @@ switch ($action) {
     case 'unenrol':
         $ue = $DB->get_record('user_enrolments', array('id'=>required_param('ue', PARAM_INT)), '*', MUST_EXIST);
         list ($instance, $plugin) = $manager->get_user_enrolment_components($ue);
-        if (!$instance || !$plugin || !$plugin->allow_unenrol_user($instance, $ue) || !has_capability("enrol/$instance->enrol:unenrol", $manager->get_context()) || !$manager->unenrol_user($ue)) {
+        if (!$instance || !$plugin || !enrol_is_enabled($instance->enrol) || !$plugin->allow_unenrol_user($instance, $ue) || !has_capability("enrol/$instance->enrol:unenrol", $manager->get_context()) || !$manager->unenrol_user($ue)) {
             throw new enrol_ajax_exception('unenrolnotpermitted');
         }
         break;
index 2c16f76..9e4ab0c 100644 (file)
@@ -45,8 +45,8 @@ class enrol_category_handler {
             return true;
         }
 
-        // Only category level roles are interesting.
-        $parentcontext = get_context_instance_by_id($ra->contextid);
+        //only category level roles are interesting
+        $parentcontext = context::instance_by_id($ra->contextid);
         if ($parentcontext->contextlevel != CONTEXT_COURSECAT) {
             return true;
         }
@@ -102,8 +102,8 @@ class enrol_category_handler {
             return true;
         }
 
-        // Only category level roles are interesting.
-        $parentcontext = get_context_instance_by_id($ra->contextid);
+        // only category level roles are interesting
+        $parentcontext = context::instance_by_id($ra->contextid);
         if ($parentcontext->contextlevel != CONTEXT_COURSECAT) {
             return true;
         }
index e9015a2..c209493 100644 (file)
@@ -46,7 +46,7 @@ class enrol_cohort_addinstance_form extends moodleform {
               ORDER BY name ASC";
         $rs = $DB->get_recordset_sql($sql, $params);
         foreach ($rs as $c) {
-            $context = get_context_instance_by_id($c->contextid);
+            $context = context::instance_by_id($c->contextid);
             if (!has_capability('moodle/cohort:view', $context)) {
                 continue;
             }
index b70b89f..4ad304e 100644 (file)
@@ -92,7 +92,7 @@ class enrol_cohort_plugin extends enrol_plugin {
               ORDER BY name ASC";
         $cohorts = $DB->get_records_sql($sql, $params);
         foreach ($cohorts as $c) {
-            $context = get_context_instance_by_id($c->contextid);
+            $context = context::instance_by_id($c->contextid);
             if (has_capability('moodle/cohort:view', $context)) {
                 return true;
             }
index 5ca237a..869e06c 100644 (file)
@@ -368,7 +368,7 @@ function enrol_cohort_get_cohorts(course_enrolment_manager $manager) {
           ORDER BY name ASC";
     $rs = $DB->get_recordset_sql($sql, $params);
     foreach ($rs as $c) {
-        $context = get_context_instance_by_id($c->contextid);
+        $context = context::instance_by_id($c->contextid);
         if (!has_capability('moodle/cohort:view', $context)) {
             continue;
         }
@@ -394,7 +394,7 @@ function enrol_cohort_can_view_cohort($cohortid) {
     global $DB;
     $cohort = $DB->get_record('cohort', array('id' => $cohortid), 'id, contextid');
     if ($cohort) {
-        $context = get_context_instance_by_id($cohort->contextid);
+        $context = context::instance_by_id($cohort->contextid);
         if (has_capability('moodle/cohort:view', $context)) {
             return true;
         }
@@ -457,7 +457,7 @@ function enrol_cohort_search_cohorts(course_enrolment_manager $manager, $offset
         // Track offset
         $offset++;
         // Check capabilities
-        $context = get_context_instance_by_id($c->contextid);
+        $context = context::instance_by_id($c->contextid);
         if (!has_capability('moodle/cohort:view', $context)) {
             continue;
         }
index 0fd4182..757362a 100644 (file)
@@ -381,7 +381,7 @@ class core_role_external extends external_api {
 
         foreach ($params['assignments'] as $assignment) {
             // Ensure the current user is allowed to run this function in the enrolment context
-            $context = get_context_instance_by_id($assignment['contextid']);
+            $context = context::instance_by_id($assignment['contextid'], IGNORE_MISSING);
             self::validate_context($context);
             require_capability('moodle/role:assign', $context);
 
@@ -445,7 +445,7 @@ class core_role_external extends external_api {
 
         foreach ($params['unassignments'] as $unassignment) {
             // Ensure the current user is allowed to run this function in the unassignment context
-            $context = get_context_instance_by_id($unassignment['contextid']);
+            $context = context::instance_by_id($unassignment['contextid'], IGNORE_MISSING);
             self::validate_context($context);
             require_capability('moodle/role:assign', $context);
 
index b19a716..596320a 100644 (file)
@@ -97,6 +97,7 @@ class course_enrolment_manager {
     private $_instances = null;
     private $_inames = null;
     private $_plugins = null;
+    private $_allplugins = null;
     private $_roles = null;
     private $_assignableroles = null;
     private $_assignablerolesothers = null;
@@ -397,11 +398,13 @@ class course_enrolment_manager {
     /**
      * Returns all of the enrolment instances for this course.
      *
+     * NOTE: since 2.4 it includes instances of disabled plugins too.
+     *
      * @return array
      */
     public function get_enrolment_instances() {
         if ($this->_instances === null) {
-            $this->_instances = enrol_get_instances($this->course->id, true);
+            $this->_instances = enrol_get_instances($this->course->id, false);
         }
         return $this->_instances;
     }
@@ -409,12 +412,14 @@ class course_enrolment_manager {
     /**
      * Returns the names for all of the enrolment instances for this course.
      *
+     * NOTE: since 2.4 it includes instances of disabled plugins too.
+     *
      * @return array
      */
     public function get_enrolment_instance_names() {
         if ($this->_inames === null) {
             $instances = $this->get_enrolment_instances();
-            $plugins = $this->get_enrolment_plugins();
+            $plugins = $this->get_enrolment_plugins(false);
             foreach ($instances as $key=>$instance) {
                 if (!isset($plugins[$instance->enrol])) {
                     // weird, some broken stuff in plugin
@@ -430,13 +435,29 @@ class course_enrolment_manager {
     /**
      * Gets all of the enrolment plugins that are active for this course.
      *
+     * @param bool $onlyenabled return only enabled enrol plugins
      * @return array
      */
-    public function get_enrolment_plugins() {
+    public function get_enrolment_plugins($onlyenabled = true) {
         if ($this->_plugins === null) {
             $this->_plugins = enrol_get_plugins(true);
         }
-        return $this->_plugins;
+
+        if ($onlyenabled) {
+            return $this->_plugins;
+        }
+
+        if ($this->_allplugins === null) {
+            // Make sure we have the same objects in _allplugins and _plugins.
+            $this->_allplugins = $this->_plugins;
+            foreach (enrol_get_plugins(false) as $name=>$plugin) {
+                if (!isset($this->_allplugins[$name])) {
+                    $this->_allplugins[$name] = $plugin;
+                }
+            }
+        }
+
+        return $this->_allplugins;
     }
 
     /**
@@ -522,7 +543,7 @@ class course_enrolment_manager {
             $userenrolment = $DB->get_record('user_enrolments', array('id'=>(int)$userenrolment));
         }
         $instances = $this->get_enrolment_instances();
-        $plugins = $this->get_enrolment_plugins();
+        $plugins = $this->get_enrolment_plugins(false);
         if (!$userenrolment || !isset($instances[$userenrolment->enrolid])) {
             return array(false, false);
         }
@@ -675,7 +696,7 @@ class course_enrolment_manager {
     }
 
     /**
-     * Gets the enrolments this user has in the course
+     * Gets the enrolments this user has in the course - including all suspended plugins and instances.
      *
      * @global moodle_database $DB
      * @param int $userid
@@ -687,7 +708,7 @@ class course_enrolment_manager {
         $params['userid'] = $userid;
         $userenrolments = $DB->get_records_select('user_enrolments', "enrolid $instancessql AND userid = :userid", $params);
         $instances = $this->get_enrolment_instances();
-        $plugins = $this->get_enrolment_plugins();
+        $plugins = $this->get_enrolment_plugins(false);
         $inames = $this->get_enrolment_instance_names();
         foreach ($userenrolments as &$ue) {
             $ue->enrolmentinstance     = $instances[$ue->enrolid];
@@ -829,6 +850,8 @@ class course_enrolment_manager {
         $url = new moodle_url($pageurl, $this->get_url_params());
         $extrafields = get_extra_user_fields($context);
 
+        $enabledplugins = $this->get_enrolment_plugins(true);
+
         $userdetails = array();
         foreach ($users as $user) {
             $details = $this->prepare_user_for_display($user, $extrafields, $now);
@@ -849,7 +872,15 @@ class course_enrolment_manager {
             // Enrolments
             $details['enrolments'] = array();
             foreach ($this->get_user_enrolments($user->id) as $ue) {
-                if ($ue->timestart and $ue->timeend) {
+                if (!isset($enabledplugins[$ue->enrolmentinstance->enrol])) {
+                    $details['enrolments'][$ue->id] = array(
+                        'text' => $ue->enrolmentinstancename,
+                        'period' => null,
+                        'dimmed' =>  true,
+                        'actions' => array()
+                    );
+                    continue;
+                } else if ($ue->timestart and $ue->timeend) {
                     $period = get_string('periodstartend', 'enrol', array('start'=>userdate($ue->timestart), 'end'=>userdate($ue->timeend)));
                     $periodoutside = ($ue->timestart && $ue->timeend && $now < $ue->timestart && $now > $ue->timeend);
                 } else if ($ue->timestart) {
@@ -909,7 +940,7 @@ class course_enrolment_manager {
     }
 
     public function get_manual_enrol_buttons() {
-        $plugins = $this->get_enrolment_plugins();
+        $plugins = $this->get_enrolment_plugins(true); // Skip disabled plugins.
         $buttons = array();
         foreach ($plugins as $plugin) {
             $newbutton = $plugin->get_manual_enrol_button($this);
@@ -941,7 +972,7 @@ class course_enrolment_manager {
      */
     public function get_filtered_enrolment_plugin() {
         $instances = $this->get_enrolment_instances();
-        $plugins = $this->get_enrolment_plugins();
+        $plugins = $this->get_enrolment_plugins(false);
 
         if (empty($this->instancefilter) || !array_key_exists($this->instancefilter, $instances)) {
             return false;
@@ -965,7 +996,7 @@ class course_enrolment_manager {
         global $DB;
 
         $instances = $this->get_enrolment_instances();
-        $plugins = $this->get_enrolment_plugins();
+        $plugins = $this->get_enrolment_plugins(false);
 
         if  (!empty($this->instancefilter)) {
             $instancesql = ' = :instanceid';
index 140b4cd..faf8ac0 100644 (file)
@@ -20,8 +20,7 @@
  * The general idea behind this file is that any errors should throw exceptions
  * which will be returned and acted upon by the calling AJAX script.
  *
- * @package    enrol
- * @subpackage manual
+ * @package    enrol_manual
  * @copyright  2010 Sam Hemelryk
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
@@ -32,8 +31,7 @@ require('../../config.php');
 require_once($CFG->dirroot.'/enrol/locallib.php');
 require_once($CFG->dirroot.'/group/lib.php');
 
-// Must have the sesskey
-$id      = required_param('id', PARAM_INT); // course id
+$id      = required_param('id', PARAM_INT); // Course id.
 $action  = required_param('action', PARAM_ALPHANUMEXT);
 
 $PAGE->set_url(new moodle_url('/enrol/ajax.php', array('id'=>$id, 'action'=>$action)));
@@ -49,7 +47,7 @@ require_login($course);
 require_capability('moodle/course:enrolreview', $context);
 require_sesskey();
 
-echo $OUTPUT->header(); // send headers
+echo $OUTPUT->header(); // Send headers.
 
 $manager = new course_enrolment_manager($PAGE, $course);
 
@@ -113,11 +111,14 @@ switch ($action) {
 
         $user = $DB->get_record('user', array('id'=>$userid), '*', MUST_EXIST);
         $instances = $manager->get_enrolment_instances();
-        $plugins = $manager->get_enrolment_plugins();
+        $plugins = $manager->get_enrolment_plugins(true); // Do not allow actions on disabled plugins.
         if (!array_key_exists($enrolid, $instances)) {
             throw new enrol_ajax_exception('invalidenrolinstance');
         }
         $instance = $instances[$enrolid];
+        if (!isset($plugins[$instance->enrol])) {
+            throw new enrol_ajax_exception('enrolnotpermitted');
+        }
         $plugin = $plugins[$instance->enrol];
         if ($plugin->allow_enrol($instance) && has_capability('enrol/'.$plugin->get_name().':enrol', $context)) {
             $plugin->enrol_user($instance, $user->id, $roleid, $timestart, $timeend);
@@ -135,4 +136,4 @@ switch ($action) {
         throw new enrol_ajax_exception('unknowajaxaction');
 }
 
-echo json_encode($outcome);
\ No newline at end of file
+echo json_encode($outcome);
index 8ee7300..05980e6 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
@@ -18,8 +17,7 @@
 /**
  * This file contains form for bulk changing user enrolments.
  *
- * @package    core
- * @subpackage enrol
+ * @package    enrol_manual
  * @copyright  2011 Sam Hemelryk
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
@@ -29,7 +27,7 @@ defined('MOODLE_INTERNAL') || die();
 require_once("$CFG->dirroot/enrol/bulkchange_forms.php");
 
 /**
- * The form to collect required information when bulk editing users enrolments
+ * The form to collect required information when bulk editing users enrolments.
  *
  * @copyright 2011 Sam Hemelryk
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
index 561ef29..5f14032 100644 (file)
@@ -26,6 +26,7 @@ defined('MOODLE_INTERNAL') || die();
 
 $capabilities = array(
 
+    /* Add, edit or remove manual enrol instance. */
     'enrol/manual:config' => array(
         'captype' => 'write',
         'contextlevel' => CONTEXT_COURSE,
@@ -34,6 +35,7 @@ $capabilities = array(
         )
     ),
 
+    /* Enrol anybody. */
     'enrol/manual:enrol' => array(
         'captype' => 'write',
         'contextlevel' => CONTEXT_COURSE,
@@ -43,6 +45,7 @@ $capabilities = array(
         )
     ),
 
+    /* Manage enrolments of users. */
     'enrol/manual:manage' => array(
         'captype' => 'write',
         'contextlevel' => CONTEXT_COURSE,
@@ -52,6 +55,7 @@ $capabilities = array(
         )
     ),
 
+    /* Unenrol anybody (including self) - watch out for data loss. */
     'enrol/manual:unenrol' => array(
         'captype' => 'write',
         'contextlevel' => CONTEXT_COURSE,
@@ -61,6 +65,7 @@ $capabilities = array(
         )
     ),
 
+    /* Unenrol self - watch out for data loss. */
     'enrol/manual:unenrolself' => array(
         'captype' => 'write',
         'contextlevel' => CONTEXT_COURSE,
@@ -69,4 +74,3 @@ $capabilities = array(
     ),
 
 );
-
index 3d99542..9958435 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
@@ -18,8 +17,7 @@
 /**
  * Manual enrol plugin installation script
  *
- * @package    enrol
- * @subpackage manual
+ * @package    enrol_manual
  * @copyright  2010 Petr Skoda {@link http://skodak.org}
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
index 978a6eb..c4ff53d 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
@@ -19,8 +18,7 @@
  * Adds new instance of enrol_manual to specified course
  * or edits current instance.
  *
- * @package    enrol
- * @subpackage manual
+ * @package    enrol_manual
  * @copyright  2010 Petr Skoda  {@link http://skodak.org}
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
@@ -49,14 +47,14 @@ $plugin = enrol_get_plugin('manual');
 if ($instances = $DB->get_records('enrol', array('courseid'=>$course->id, 'enrol'=>'manual'), 'id ASC')) {
     $instance = array_shift($instances);
     if ($instances) {
-        // oh - we allow only one instance per course!!
+        // Oh - we allow only one instance per course!!
         foreach ($instances as $del) {
             $plugin->delete_instance($del);
         }
     }
 } else {
     require_capability('moodle/course:enrolconfig', $context);
-    // no instance yet, we have to add new instance
+    // No instance yet, we have to add new instance.
     navigation_node::override_active_url(new moodle_url('/enrol/instances.php', array('id'=>$course->id)));
     $instance = new stdClass();
     $instance->id       = null;
index 7ff2792..eb21099 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
@@ -19,8 +18,7 @@
  * Adds new instance of enrol_manual to specified course
  * or edits current instance.
  *
- * @package    enrol
- * @subpackage manual
+ * @package    enrol_manual
  * @copyright  2010 Petr Skoda  {@link http://skodak.org}
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
@@ -62,4 +60,4 @@ class enrol_manual_edit_form extends moodleform {
 
         $this->set_data($instance);
     }
-}
\ No newline at end of file
+}
index 2d8a700..538f458 100644 (file)
  * This page allows the current user to edit a manual user enrolment.
  * It is not compatible with the frontpage.
  *
- * @package    enrol
- * @subpackage manual
+ * @package    enrol_manual
  * @copyright  2011 Sam Hemelryk
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
 require('../../config.php');
 require_once("$CFG->dirroot/enrol/locallib.php");
-require_once("$CFG->dirroot/enrol/renderer.php"); // Required for the course enrolment manager table
+require_once("$CFG->dirroot/enrol/renderer.php"); // Required for the course enrolment manager table.
 require_once("$CFG->dirroot/enrol/manual/editenrolment_form.php");
 
-$ueid   = required_param('ue', PARAM_INT); // user enrolment id
+$ueid   = required_param('ue', PARAM_INT); // User enrolment id.
 $filter = optional_param('ifilter', 0, PARAM_INT);
 
-// Get the user enrolment object
-$ue     = $DB->get_record('user_enrolments', array('id' => $ueid), '*', MUST_EXIST);
-// Get the user for whom the enrolment is
-$user   = $DB->get_record('user', array('id'=>$ue->userid), '*', MUST_EXIST);
-// Get the course the enrolment is to
-list($ctxsql, $ctxjoin) = context_instance_preload_sql('c.id', CONTEXT_COURSE, 'ctx');
-$sql = "SELECT c.* $ctxsql
+// Get the user enrolment object.
+$ue = $DB->get_record('user_enrolments', array('id' => $ueid), '*', MUST_EXIST);
+// Get the user for whom the enrolment is.
+$user = $DB->get_record('user', array('id'=>$ue->userid), '*', MUST_EXIST);
+// Get the course the enrolment is to.
+$sql = "SELECT c.*
           FROM {course} c
-     LEFT JOIN {enrol} e ON e.courseid = c.id
-               $ctxjoin
+          JOIN {enrol} e ON e.courseid = c.id
          WHERE e.id = :enrolid";
 $params = array('enrolid' => $ue->enrolid);
 $course = $DB->get_record_sql($sql, $params, MUST_EXIST);
-context_instance_preload($course);
 
-// Make sure its not the front page course
+// Make sure its not the front page course.
 if ($course->id == SITEID) {
     redirect(new moodle_url('/'));
 }
 
-// Obviously
+// Obviously.
 require_login($course);
-// Make sure the user can manage manual enrolments for this course
+// Make sure the user can manage manual enrolments for this course.
 require_capability("enrol/manual:manage", context_course::instance($course->id, MUST_EXIST));
 
-// Get the enrolment manager for this course
+// Get the enrolment manager for this course.
 $manager = new course_enrolment_manager($PAGE, $course, $filter);
 // Get an enrolment users table object. Doign this will automatically retrieve the the URL params
 // relating to table the user was viewing before coming here, and allows us to return the user to the
@@ -70,7 +66,7 @@ $table = new course_enrolment_users_table($manager, $PAGE);
 $usersurl = new moodle_url('/enrol/users.php', array('id' => $course->id));
 // The URl to return the user too after this screen.
 $returnurl = new moodle_url($usersurl, $manager->get_url_params()+$table->get_url_params());
-// The URL of this page
+// The URL of this page.
 $url = new moodle_url('/enrol/manual/editenrolment.php', $returnurl->params());
 
 $PAGE->set_url($url);
@@ -88,9 +84,7 @@ $mform->set_data($PAGE->url->params());
 // Check the form hasn't been cancelled
 if ($mform->is_cancelled()) {
     redirect($returnurl);
-} else if ($mform->is_submitted() && $mform->is_validated() && confirm_sesskey()) {
-    // The forms been submit, validated and the sesskey has been checked ... edit the enrolment.
-    $data = $mform->get_data();
+} else if ($data = $mform->get_data()) {
     if ($manager->edit_enrolment($ue, $data)) {
         redirect($returnurl);
     }
@@ -107,4 +101,4 @@ $PAGE->navbar->add($fullname);
 echo $OUTPUT->header();
 echo $OUTPUT->heading($fullname);
 $mform->display();
-echo $OUTPUT->footer();
\ No newline at end of file
+echo $OUTPUT->footer();
index ea9205d..b73fc7c 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
@@ -18,8 +17,7 @@
 /**
  * Contains the form used to edit manual enrolments for a user.
  *
- * @package    enrol
- * @subpackage manual
+ * @package    enrol_manual
  * @copyright  2011 Sam Hemelryk
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
@@ -80,4 +78,4 @@ class enrol_manual_user_enrolment_form extends moodleform {
 
         return $errors;
     }
-}
\ No newline at end of file
+}
index 94fde81..01686da 100644 (file)
@@ -14,7 +14,6 @@
 // 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.
  *
@@ -32,7 +31,7 @@ defined('MOODLE_INTERNAL') || die();
 require_once("$CFG->libdir/externallib.php");
 
 /**
- * Manual enrolment external functions
+ * Manual enrolment external functions.
  *
  * @package    enrol_manual
  * @category   external
@@ -43,7 +42,7 @@ require_once("$CFG->libdir/externallib.php");
 class enrol_manual_external extends external_api {
 
     /**
-     * Returns description of method parameters
+     * Returns description of method parameters.
      *
      * @return external_function_parameters
      * @since Moodle 2.2
@@ -68,7 +67,7 @@ class enrol_manual_external extends external_api {
     }
 
     /**
-     * Enrolment of users
+     * Enrolment of users.
      *
      * Function throw an exception at the first error encountered.
      * @param array $enrolments  An array of user enrolment
@@ -82,24 +81,24 @@ class enrol_manual_external extends external_api {
         $params = self::validate_parameters(self::enrol_users_parameters(),
                 array('enrolments' => $enrolments));
 
-        $transaction = $DB->start_delegated_transaction(); //rollback all enrolment if an error occurs
-                                                           //(except if the DB doesn't support it)
+        $transaction = $DB->start_delegated_transaction(); // Rollback all enrolment if an error occurs
+                                                           // (except if the DB doesn't support it).
 
-        //retrieve the manual enrolment plugin
+        // Retrieve the manual enrolment plugin.
         $enrol = enrol_get_plugin('manual');
         if (empty($enrol)) {
             throw new moodle_exception('manualpluginnotinstalled', 'enrol_manual');
         }
 
         foreach ($params['enrolments'] as $enrolment) {
-            // Ensure the current user is allowed to run this function in the enrolment context
+            // Ensure the current user is allowed to run this function in the enrolment context.
             $context = context_course::instance($enrolment['courseid'], IGNORE_MISSING);
             self::validate_context($context);
 
-            //check that the user has the permission to manual enrol
+            // Check that the user has the permission to manual enrol.
             require_capability('enrol/manual:enrol', $context);
 
-            //throw an exception if user is not able to assign the role
+            // Throw an exception if user is not able to assign the role.
             $roles = get_assignable_roles($context);
             if (!key_exists($enrolment['roleid'], $roles)) {
                 $errorparams = new stdClass();
@@ -109,7 +108,7 @@ class enrol_manual_external extends external_api {
                 throw new moodle_exception('wsusercannotassign', 'enrol_manual', '', $errorparams);
             }
 
-            //check manual enrolment plugin instance is enabled/exist
+            // Check manual enrolment plugin instance is enabled/exist.
             $enrolinstances = enrol_get_instances($enrolment['courseid'], true);
             foreach ($enrolinstances as $courseenrolinstance) {
               if ($courseenrolinstance->enrol == "manual") {
@@ -123,7 +122,7 @@ class enrol_manual_external extends external_api {
               throw new moodle_exception('wsnoinstance', 'enrol_manual', $errorparams);
             }
 
-            //check that the plugin accept enrolment (it should always the case, it's hard coded in the plugin)
+            // Check that the plugin accept enrolment (it should always the case, it's hard coded in the plugin).
             if (!$enrol->allow_enrol($instance)) {
                 $errorparams = new stdClass();
                 $errorparams->roleid = $enrolment['roleid'];
@@ -132,7 +131,7 @@ class enrol_manual_external extends external_api {
                 throw new moodle_exception('wscannotenrol', 'enrol_manual', '', $errorparams);
             }
 
-            //finally proceed the enrolment
+            // Finally proceed the enrolment.
             $enrolment['timestart'] = isset($enrolment['timestart']) ? $enrolment['timestart'] : 0;
             $enrolment['timeend'] = isset($enrolment['timeend']) ? $enrolment['timeend'] : 0;
             $enrolment['status'] = (isset($enrolment['suspend']) && !empty($enrolment['suspend'])) ?
@@ -147,7 +146,7 @@ class enrol_manual_external extends external_api {
     }
 
     /**
-     * Returns description of method result value
+     * Returns description of method result value.
      *
      * @return null
      * @since Moodle 2.2
@@ -159,7 +158,7 @@ class enrol_manual_external extends external_api {
 }
 
 /**
- * Deprecated manual enrolment external functions
+ * Deprecated manual enrolment external functions.
  *
  * @package    enrol_manual
  * @copyright  2011 Jerome Mouneyrac
@@ -172,7 +171,7 @@ class enrol_manual_external extends external_api {
 class moodle_enrol_manual_external extends external_api {
 
     /**
-     * Returns description of method parameters
+     * Returns description of method parameters.
      *
      * @return external_function_parameters
      * @since Moodle 2.0
@@ -199,7 +198,7 @@ class moodle_enrol_manual_external extends external_api {
     }
 
     /**
-     * Returns description of method result value
+     * Returns description of method result value.
      *
      * @return nul
      * @since Moodle 2.0
index 5320f6a..2cf94bb 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
@@ -18,8 +17,7 @@
 /**
  * Manual enrolment plugin main library file.
  *
- * @package    enrol
- * @subpackage manual
+ * @package    enrol_manual
  * @copyright  2010 Petr Skoda {@link http://skodak.org}
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
@@ -29,22 +27,22 @@ defined('MOODLE_INTERNAL') || die();
 class enrol_manual_plugin extends enrol_plugin {
 
     public function roles_protected() {
-        // users may tweak the roles later
+        // Users may tweak the roles later.
         return false;
     }
 
     public function allow_enrol(stdClass $instance) {
-        // users with enrol cap may unenrol other users manually manually
+        // Users with enrol cap may unenrol other users manually manually.
         return true;
     }
 
     public function allow_unenrol(stdClass $instance) {
-        // users with unenrol cap may unenrol other users manually manually
+        // Users with unenrol cap may unenrol other users manually manually.
         return true;
     }
 
     public function allow_manage(stdClass $instance) {
-        // users with manage cap may tweak period and status
+        // Users with manage cap may tweak period and status.
         return true;
     }
 
@@ -52,7 +50,7 @@ class enrol_manual_plugin extends enrol_plugin {
      * Returns link to manual enrol UI if exists.
      * Does the access control tests automatically.
      *
-     * @param object $instance
+     * @param stdClass $instance
      * @return moodle_url
      */
     public function get_manual_enrol_link($instance) {
@@ -96,7 +94,7 @@ class enrol_manual_plugin extends enrol_plugin {
     }
 
     /**
-     * Returns edit icons for the page with list of instances
+     * Returns edit icons for the page with list of instances.
      * @param stdClass $instance
      * @return array
      */
@@ -145,7 +143,7 @@ class enrol_manual_plugin extends enrol_plugin {
 
     /**
      * Add new instance of enrol plugin with default settings.
-     * @param object $course
+     * @param stdClass $course
      * @return int id of new instance, null if can not be created
      */
     public function add_default_instance($course) {
@@ -155,7 +153,7 @@ class enrol_manual_plugin extends enrol_plugin {
 
     /**
      * Add new instance of enrol plugin.
-     * @param object $course
+     * @param stdClass $course
      * @param array instance fields
      * @return int id of new instance, null if can not be created
      */
@@ -260,7 +258,7 @@ class enrol_manual_plugin extends enrol_plugin {
     }
 
     /**
-     * Gets an array of the user enrolment actions
+     * Gets an array of the user enrolment actions.
      *
      * @param course_enrolment_manager $manager
      * @param stdClass $ue A user enrolment object
@@ -284,7 +282,7 @@ class enrol_manual_plugin extends enrol_plugin {
     }
 
     /**
-     * The manual plugin has several bulk operations that can be performed
+     * The manual plugin has several bulk operations that can be performed.
      * @param course_enrolment_manager $manager
      * @return array
      */
index 1d09681..1a3d580 100644 (file)
@@ -17,8 +17,7 @@
 /**
  * Auxiliary manual user enrolment lib, the main purpose is to lower memory requirements...
  *
- * @package    enrol
- * @subpackage manual
+ * @package    enrol_manual
  * @copyright  2010 Petr Skoda {@link http://skodak.org}
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
@@ -30,7 +29,7 @@ require_once($CFG->dirroot . '/enrol/locallib.php');
 
 
 /**
- * Enrol candidates
+ * Enrol candidates.
  */
 class enrol_manual_potential_participant extends user_selector_base {
     protected $enrolid;
@@ -42,12 +41,12 @@ class enrol_manual_potential_participant extends user_selector_base {
 
     /**
      * Candidate users
-     * @param <type> $search
+     * @param string $search
      * @return array
      */
     public function find_users($search) {
         global $DB;
-        //by default wherecondition retrieves all users except the deleted, not confirmed and guest
+        // By default wherecondition retrieves all users except the deleted, not confirmed and guest.
         list($wherecondition, $params) = $this->search_sql($search, 'u');
         $params['enrolid'] = $this->enrolid;
 
@@ -92,7 +91,7 @@ class enrol_manual_potential_participant extends user_selector_base {
 }
 
 /**
- * Enroled users
+ * Enrolled users.
  */
 class enrol_manual_current_participant extends user_selector_base {
     protected $courseid;
@@ -105,12 +104,12 @@ class enrol_manual_current_participant extends user_selector_base {
 
     /**
      * Candidate users
-     * @param <type> $search
+     * @param string $search
      * @return array
      */
     public function find_users($search) {
         global $DB;
-        //by default wherecondition retrieves all users except the deleted, not confirmed and guest
+        // By default wherecondition retrieves all users except the deleted, not confirmed and guest.
         list($wherecondition, $params) = $this->search_sql($search, 'u');
         $params['enrolid'] = $this->enrolid;
 
@@ -182,7 +181,6 @@ class enrol_manual_editselectedusers_operation extends enrol_bulk_enrolment_oper
     /**
      * Processes the bulk operation request for the given userids with the provided properties.
      *
-     * @global moodle_database $DB
      * @param course_enrolment_manager $manager
      * @param array $userids
      * @param stdClass $properties The data returned by the form.
@@ -194,7 +192,7 @@ class enrol_manual_editselectedusers_operation extends enrol_bulk_enrolment_oper
             return false;
         }
 
-        // Get all of the user enrolment id's
+        // Get all of the user enrolment id's.
         $ueids = array();
         $instances = array();
         foreach ($users as $user) {
@@ -237,15 +235,15 @@ class enrol_manual_editselectedusers_operation extends enrol_bulk_enrolment_oper
             return true;
         }
 
-        // Update the modifierid
+        // Update the modifierid.
         $updatesql[] = 'modifierid = :modifierid';
         $params['modifierid'] = (int)$USER->id;
 
-        // Update the time modified
+        // Update the time modified.
         $updatesql[] = 'timemodified = :timemodified';
         $params['timemodified'] = time();
 
-        // Build the SQL statement
+        // Build the SQL statement.
         $updatesql = join(', ', $updatesql);
         $sql = "UPDATE {user_enrolments}
                    SET $updatesql
@@ -353,4 +351,4 @@ class enrol_manual_deleteselectedusers_operation extends enrol_bulk_enrolment_op
         }
         return true;
     }
-}
\ No newline at end of file
+}
index 0a4f698..46d1b77 100644 (file)
@@ -17,8 +17,7 @@
 /**
  * Manual user enrolment UI.
  *
- * @package    enrol
- * @subpackage manual
+ * @package    enrol_manual
  * @copyright  2010 Petr Skoda {@link http://skodak.org}
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
@@ -47,7 +46,7 @@ $roles = get_assignable_roles($context);
 $roles = array('0'=>get_string('none')) + $roles;
 
 if (!isset($roles[$roleid])) {
-    // weird - security always first!
+    // Weird - security always first!
     $roleid = 0;
 }
 
@@ -88,14 +87,14 @@ $timeformat = get_string('strftimedatefullshort');
 $today = time();
 $today = make_timestamp(date('Y', $today), date('m', $today), date('d', $today), 0, 0, 0);
 
-// enrolment start
+// Enrolment start.
 $basemenu = array();
 if ($course->startdate > 0) {
     $basemenu[2] = get_string('coursestart') . ' (' . userdate($course->startdate, $timeformat) . ')';
 }
 $basemenu[3] = get_string('today') . ' (' . userdate($today, $timeformat) . ')' ;
 
-// process add and removes
+// Process add and removes.
 if (optional_param('add', false, PARAM_BOOL) && confirm_sesskey()) {
     $userstoassign = $potentialuserselector->get_selected_users();
     if (!empty($userstoassign)) {
@@ -126,7 +125,7 @@ if (optional_param('add', false, PARAM_BOOL) && confirm_sesskey()) {
     }
 }
 
-// Process incoming role unassignments
+// Process incoming role unassignments.
 if (optional_param('remove', false, PARAM_BOOL) && confirm_sesskey()) {
     $userstounassign = $currentuserselector->get_selected_users();
     if (!empty($userstounassign)) {
index 13d72ed..79686aa 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
@@ -18,8 +17,7 @@
 /**
  * Manual enrolment plugin settings and presets.
  *
- * @package    enrol
- * @subpackage manual
+ * @package    enrol_manual
  * @copyright  2010 Petr Skoda {@link http://skodak.org}
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
@@ -55,4 +53,3 @@ if ($ADMIN->fulltree) {
             get_string('defaultrole', 'role'), '', $student->id, $options));
     }
 }
-
index fb1744d..c7304fb 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
@@ -18,8 +17,7 @@
 /**
  * Manual enrolment plugin - support for user self unenrolment.
  *
- * @package    enrol
- * @subpackage manual
+ * @package    enrol_manual
  * @copyright  2010 Petr Skoda {@link http://skodak.org}
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
@@ -41,7 +39,7 @@ require_login($course);
 
 $plugin = enrol_get_plugin('manual');
 
-// security defined inside following function
+// Security defined inside following function.
 if (!$plugin->get_unenrolself_link($instance)) {
     redirect(new moodle_url('/course/view.php', array('id'=>$course->id)));
 }
@@ -51,7 +49,7 @@ $PAGE->set_title($plugin->get_instance_name($instance));
 
 if ($confirm and confirm_sesskey()) {
     $plugin->unenrol_user($instance, $USER->id);
-    add_to_log($course->id, 'course', 'unenrol', '../enrol/users.php?id='.$course->id, $course->id); //there should be userid somewhere!
+    add_to_log($course->id, 'course', 'unenrol', '../enrol/users.php?id='.$course->id, $course->id); //TODO: there should be userid somewhere!
     redirect(new moodle_url('/index.php'));
 }
 
index 8d2ff58..2107a67 100644 (file)
@@ -17,8 +17,7 @@
 /**
  * Manual enrolment plugin version specification.
  *
- * @package    enrol
- * @subpackage manual
+ * @package    enrol_manual
  * @copyright  2010 Petr Skoda {@link http://skodak.org}
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
index fafbfd7..c7fa8ea 100644 (file)
@@ -449,7 +449,7 @@ class course_enrolment_table extends html_table implements renderable {
 
         // Collect the bulk operations for the currently filtered plugin if there is one.
         $plugin = $manager->get_filtered_enrolment_plugin();
-        if ($plugin) {
+        if ($plugin and enrol_is_enabled($plugin->get_name())) {
             $this->bulkoperations = $plugin->get_bulk_operations($manager);
         }
     }
index 314720c..9cdd3c3 100644 (file)
@@ -26,6 +26,7 @@ defined('MOODLE_INTERNAL') || die();
 
 $capabilities = array(
 
+    /* Add or edit enrol-self instance in course. */
     'enrol/self:config' => array(
 
         'captype' => 'write',
@@ -36,6 +37,7 @@ $capabilities = array(
         )
     ),
 
+    /* Manage user self-enrolments. */
     'enrol/self:manage' => array(
 
         'captype' => 'write',
@@ -46,6 +48,7 @@ $capabilities = array(
         )
     ),
 
+    /* Voluntarily unenrol self from course - watch out for data loss. */
     'enrol/self:unenrolself' => array(
         'captype' => 'write',
         'contextlevel' => CONTEXT_COURSE,
@@ -54,6 +57,7 @@ $capabilities = array(
         )
     ),
 
+    /* Unenrol anybody from course (including self) -  watch out for data loss. */
     'enrol/self:unenrol' => array(
         'captype' => 'write',
         'contextlevel' => CONTEXT_COURSE,
@@ -64,5 +68,3 @@ $capabilities = array(
     ),
 
 );
-
-
index 82d8c2f..320fc08 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
@@ -18,8 +17,7 @@
 /**
  * Self enrol plugin installation script
  *
- * @package    enrol
- * @subpackage self
+ * @package    enrol_self
  * @copyright  2010 Petr Skoda {@link http://skodak.org}
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
index 4b38df4..97448ca 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
@@ -19,8 +18,7 @@
  * Adds new instance of enrol_self to specified course
  * or edits current instance.
  *
- * @package    enrol
- * @subpackage self
+ * @package    enrol_self
  * @copyright  2010 Petr Skoda  {@link http://skodak.org}
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
@@ -29,7 +27,7 @@ require('../../config.php');
 require_once('edit_form.php');
 
 $courseid   = required_param('courseid', PARAM_INT);
-$instanceid = optional_param('id', 0, PARAM_INT); // instanceid
+$instanceid = optional_param('id', 0, PARAM_INT);
 
 $course = $DB->get_record('course', array('id'=>$courseid), '*', MUST_EXIST);
 $context = context_course::instance($course->id, MUST_EXIST);
@@ -51,11 +49,12 @@ if ($instanceid) {
     $instance = $DB->get_record('enrol', array('courseid'=>$course->id, 'enrol'=>'self', 'id'=>$instanceid), '*', MUST_EXIST);
 } else {
     require_capability('moodle/course:enrolconfig', $context);
-    // no instance yet, we have to add new instance
+    // No instance yet, we have to add new instance.
     navigation_node::override_active_url(new moodle_url('/enrol/instances.php', array('id'=>$course->id)));
     $instance = new stdClass();
-    $instance->id       = null;
-    $instance->courseid = $course->id;
+    $instance->id         = null;
+    $instance->courseid   = $course->id;
+    $instance->customint5 = 0;
 }
 
 $mform = new enrol_self_edit_form(NULL, array($instance, $plugin, $context));
@@ -74,6 +73,7 @@ if ($mform->is_cancelled()) {
         $instance->customint2     = $data->customint2;
         $instance->customint3     = $data->customint3;
         $instance->customint4     = $data->customint4;
+        $instance->customint5     = $data->customint5;
         $instance->customtext1    = $data->customtext1;
         $instance->roleid         = $data->roleid;
         $instance->enrolperiod    = $data->enrolperiod;
@@ -88,7 +88,7 @@ if ($mform->is_cancelled()) {
 
     } else {
         $fields = array('status'=>$data->status, 'name'=>$data->name, 'password'=>$data->password, 'customint1'=>$data->customint1, 'customint2'=>$data->customint2,
-                        'customint3'=>$data->customint3, 'customint4'=>$data->customint4, 'customtext1'=>$data->customtext1,
+                        'customint3'=>$data->customint3, 'customint4'=>$data->customint4, 'customint5'=>$data->customint5, 'customtext1'=>$data->customtext1,
                         'roleid'=>$data->roleid, 'enrolperiod'=>$data->enrolperiod, 'enrolstartdate'=>$data->enrolstartdate, 'enrolenddate'=>$data->enrolenddate);
         $plugin->add_instance($course, $fields);
     }
index 7f77cfc..f094b9b 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
@@ -19,8 +18,7 @@
  * Adds new instance of enrol_self to specified course
  * or edits current instance.
  *
- * @package    enrol
- * @subpackage self
+ * @package    enrol_self
  * @copyright  2010 Petr Skoda  {@link http://skodak.org}
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
@@ -32,6 +30,8 @@ require_once($CFG->libdir.'/formslib.php');
 class enrol_self_edit_form extends moodleform {
 
     function definition() {
+        global $DB;
+
         $mform = $this->_form;
 
         list($instance, $plugin, $context) = $this->_customdata;
@@ -100,6 +100,38 @@ class enrol_self_edit_form extends moodleform {
         $mform->addHelpButton('customint3', 'maxenrolled', 'enrol_self');
         $mform->setType('customint3', PARAM_INT);
 
+        $cohorts = array(0 => get_string('no'));
+        list($sqlparents, $params) = $DB->get_in_or_equal($context->get_parent_context_ids(), SQL_PARAMS_NAMED);
+        $params['current'] = $instance->customint5;
+        $sql = "SELECT id, name, idnumber, contextid
+                  FROM {cohort}
+                 WHERE contextid $sqlparents OR id = :current
+              ORDER BY name ASC, idnumber ASC";
+        $rs = $DB->get_recordset_sql($sql, $params);
+        foreach ($rs as $c) {
+            $ccontext = context::instance_by_id($c->contextid);
+            if ($c->id != $instance->customint5 and !has_capability('moodle/cohort:view', $ccontext)) {
+                continue;
+            }
+            $cohorts[$c->id] = format_string($c->name, true, array('context'=>$context));
+            if ($c->idnumber) {
+                $cohorts[$c->id] .= ' ['.s($c->idnumber).']';
+            }
+        }
+        if (!isset($cohorts[$instance->customint5])) {
+            // Somebody deleted a cohort, better keep the wrong value so that random ppl can not enrol.
+            $cohorts[$instance->customint5] = get_string('unknowncohort', 'cohort', $instance->customint5);
+        }
+        $rs->close();
+        if (count($cohorts) > 1) {
+            $mform->addElement('select', 'customint5', get_string('cohortonly', 'enrol_self'), $cohorts);
+            $mform->addHelpButton('customint5', 'cohortonly', 'enrol_self');
+        } else {
+            $mform->addElement('hidden', 'customint5');
+            $mform->setType('customint5', PARAM_INT);
+            $mform->setConstant('customint5', 0);
+        }
+
         $mform->addElement('advcheckbox', 'customint4', get_string('sendcoursewelcomemessage', 'enrol_self'));
         $mform->setDefault('customint4', $plugin->get_config('sendcoursewelcomemessage'));
         $mform->addHelpButton('customint4', 'sendcoursewelcomemessage', 'enrol_self');
@@ -159,7 +191,7 @@ class enrol_self_edit_form extends moodleform {
     }
 
     /**
-    * Gets a list of roles that this user can assign for the course as the default for self-enrolment
+    * Gets a list of roles that this user can assign for the course as the default for self-enrolment.
     *
     * @param context $context the context.
     * @param integer $defaultrole the id of the role that is set as the default for self-enrolment
index 418359e..8af223f 100644 (file)
  * This page allows the current user to edit a self user enrolment.
  * It is not compatible with the frontpage.
  *
- * @package    enrol
- * @subpackage self
+ * @package    enrol_self
  * @copyright  2011 Sam Hemelryk
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
 require('../../config.php');
-require_once("$CFG->dirroot/enrol/locallib.php"); // Required for the course enrolment manager
-require_once("$CFG->dirroot/enrol/renderer.php"); // Required for the course enrolment users table
-require_once("$CFG->dirroot/enrol/self/editenrolment_form.php"); // Forms for this page
-
-$ueid   = required_param('ue', PARAM_INT); // user enrolment id
-$filter = optional_param('ifilter', 0, PARAM_INT); // table filter for return url
-
-// Get the user enrolment object
-$ue     = $DB->get_record('user_enrolments', array('id' => $ueid), '*', MUST_EXIST);
-// Get the user for whom the enrolment is
-$user   = $DB->get_record('user', array('id'=>$ue->userid), '*', MUST_EXIST);
-// Get the course the enrolment is to
-list($ctxsql, $ctxjoin) = context_instance_preload_sql('c.id', CONTEXT_COURSE, 'ctx');
-$sql = "SELECT c.* $ctxsql
+require_once("$CFG->dirroot/enrol/locallib.php"); // Required for the course enrolment manager.
+require_once("$CFG->dirroot/enrol/renderer.php"); // Required for the course enrolment users table.
+require_once("$CFG->dirroot/enrol/self/editenrolment_form.php"); // Forms for this page.
+
+$ueid   = required_param('ue', PARAM_INT);
+$filter = optional_param('ifilter', 0, PARAM_INT); // Table filter for return url.
+
+// Get the user enrolment object.
+$ue = $DB->get_record('user_enrolments', array('id' => $ueid), '*', MUST_EXIST);
+// Get the user for whom the enrolment is.
+$user = $DB->get_record('user', array('id'=>$ue->userid), '*', MUST_EXIST);
+// Get the course the enrolment is to.
+$sql = "SELECT c.*
           FROM {course} c
-     LEFT JOIN {enrol} e ON e.courseid = c.id
-               $ctxjoin
+          JOIN {enrol} e ON e.courseid = c.id
          WHERE e.id = :enrolid";
 $params = array('enrolid' => $ue->enrolid);
 $course = $DB->get_record_sql($sql, $params, MUST_EXIST);
-context_instance_preload($course);
 
-// Make sure the course isn't the front page
+// Make sure the course isn't the front page.
 if ($course->id == SITEID) {
     redirect(new moodle_url('/'));
 }
 
-// Obvioulsy
+// Do not allow any changes if plugin disabled.
+if (!enrol_is_enabled('self')) {
+    redirect(new moodle_url('/course/view.php', array('id'=>$course->id)));
+}
+
+// Obviously.
 require_login($course);
-// The user must be able to manage self enrolments within the course
+// The user must be able to manage self enrolments within the course.
 require_capability("enrol/self:manage", context_course::instance($course->id, MUST_EXIST));
 
-// Get the enrolment manager for this course
+// Get the enrolment manager for this course.
 $manager = new course_enrolment_manager($PAGE, $course, $filter);
-// Get an enrolment users table object. Doign this will automatically retrieve the the URL params
+// Get an enrolment users table object. Doing this will automatically retrieve the the URL params
 // relating to table the user was viewing before coming here, and allows us to return the user to the
 // exact page of the users screen they can from.
 $table = new course_enrolment_users_table($manager, $PAGE);
@@ -70,30 +71,28 @@ $table = new course_enrolment_users_table($manager, $PAGE);
 $usersurl = new moodle_url('/enrol/users.php', array('id' => $course->id));
 // The URl to return the user too after this screen.
 $returnurl = new moodle_url($usersurl, $manager->get_url_params()+$table->get_url_params());
-// The URL of this page
+// The URL of this page.
 $url = new moodle_url('/enrol/self/editenrolment.php', $returnurl->params());
 
 $PAGE->set_url($url);
 $PAGE->set_pagelayout('admin');
 navigation_node::override_active_url($usersurl);
 
-// Gets the compontents of the user enrolment
+// Gets the components of the user enrolment.
 list($instance, $plugin) = $manager->get_user_enrolment_components($ue);
-// Check that the user can manage this instance, and that the instance is of the correct type
+// Check that the user can manage this instance, and that the instance is of the correct type.
 if (!$plugin->allow_manage($instance) || $instance->enrol != 'self' || !($plugin instanceof enrol_self_plugin)) {
     print_error('erroreditenrolment', 'enrol');
 }
 
-// Get the self enrolment edit form
+// Get the self enrolment edit form.
 $mform = new enrol_self_user_enrolment_form($url, array('user'=>$user, 'course'=>$course, 'ue'=>$ue));
 $mform->set_data($PAGE->url->params());
 
-// Check the form hasn't been cancelled
 if ($mform->is_cancelled()) {
     redirect($returnurl);
-} else if ($mform->is_submitted() && $mform->is_validated() && confirm_sesskey()) {
-    // The forms been submit, validated and the sesskey has been checked ... edit the enrolment.
-    $data = $mform->get_data();
+
+} else if ($data = $mform->get_data()) {
     if ($manager->edit_enrolment($ue, $data)) {
         redirect($returnurl);
     }
@@ -110,4 +109,4 @@ $PAGE->navbar->add($fullname);
 echo $OUTPUT->header();
 echo $OUTPUT->heading($fullname);
 $mform->display();
-echo $OUTPUT->footer();
\ No newline at end of file
+echo $OUTPUT->footer();
index 0143122..24d6525 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
@@ -18,8 +17,7 @@
 /**
  * Contains the form used to edit self enrolments for a user.
  *
- * @package    enrol
- * @subpackage self
+ * @package    enrol_self
  * @copyright  2011 Sam Hemelryk
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
@@ -80,4 +78,4 @@ class enrol_self_user_enrolment_form extends moodleform {
 
         return $errors;
     }
-}
\ No newline at end of file
+}
index 95595fc..06ba0e4 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
 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
 /**
- * Strings for component 'enrol_self', language 'en', branch 'MOODLE_20_STABLE'
+ * Strings for component 'enrol_self', language 'en'.
  *
- * @package    enrol
- * @subpackage self
+ * @package    enrol_self
  * @copyright  2010 Petr Skoda  {@link http://skodak.org}
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
+$string['cohortnonmemberinfo'] = 'Only members of cohort \'{$a}\' can self-enrol.';
+$string['cohortonly'] = 'Only cohort members';
+$string['cohortonly_help'] = 'Self enrolment may be restricted to members of a specified cohort only. Note that changing this setting has no effect on existing enrolments.';
 $string['customwelcomemessage'] = 'Custom welcome message';
 $string['customwelcomemessage_help'] = 'A custom welcome message may be added as plain text or Moodle-auto format, including HTML tags and multi-lang tags.
 
index 771ec76..fda2414 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
@@ -18,8 +17,7 @@
 /**
  * Self enrolment plugin.
  *
- * @package    enrol
- * @subpackage self
+ * @package    enrol_self
  * @copyright  2010 Petr Skoda  {@link http://skodak.org}
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
@@ -66,7 +64,7 @@ class enrol_self_plugin extends enrol_plugin {
     /**
      * Returns localised name of enrol instance
      *
-     * @param object $instance (null is accepted too)
+     * @param stdClass $instance (null is accepted too)
      * @return string
      */
     public function get_instance_name($instance) {
@@ -86,28 +84,38 @@ class enrol_self_plugin extends enrol_plugin {
     }
 
     public function roles_protected() {
-        // users may tweak the roles later
+        // Users may tweak the roles later.
         return false;
     }
 
     public function allow_unenrol(stdClass $instance) {
-        // users with unenrol cap may unenrol other users manually manually
+        // Users with unenrol cap may unenrol other users manually manually.
         return true;
     }
 
     public function allow_manage(stdClass $instance) {
-        // users with manage cap may tweak period and status
+        // Users with manage cap may tweak period and status.
         return true;
     }
 
     public function show_enrolme_link(stdClass $instance) {
-        return ($instance->status == ENROL_INSTANCE_ENABLED);
+        global $CFG, $USER;
+
+        if ($instance->status != ENROL_INSTANCE_ENABLED) {
+            return false;
+        }
+        if ($instance->customint5) {
+            require_once("$CFG->dirroot/cohort/lib.php");
+            return cohort_is_member($instance->customint5, $USER->id);
+        }
+        return true;
     }
 
     /**
      * Sets up navigation entries.
      *
-     * @param object $instance
+     * @param stdClass $instancesnode
+     * @param stdClass $instance
      * @return void
      */
     public function add_course_navigation($instancesnode, stdClass $instance) {
@@ -156,7 +164,7 @@ class enrol_self_plugin extends enrol_plugin {
         if (!has_capability('moodle/course:enrolconfig', $context) or !has_capability('enrol/self:config', $context)) {
             return NULL;
         }
-        // multiple instances supported - different roles with different password
+        // Multiple instances supported - different roles with different password.
         return new moodle_url('/enrol/self/edit.php', array('courseid'=>$courseid));
     }
 
@@ -171,7 +179,7 @@ class enrol_self_plugin extends enrol_plugin {
         global $CFG, $OUTPUT, $SESSION, $USER, $DB;
 
         if (isguestuser()) {
-            // can not enrol guest!!
+            // Can not enrol guest!!
             return null;
         }
         if ($DB->record_exists('user_enrolments', array('userid'=>$USER->id, 'enrolid'=>$instance->id))) {
@@ -189,6 +197,18 @@ class enrol_self_plugin extends enrol_plugin {
             return null;
         }
 
+        if ($instance->customint5) {
+            require_once("$CFG->dirroot/cohort/lib.php");
+            if (!cohort_is_member($instance->customint5, $USER->id)) {
+                $cohort = $DB->get_record('cohort', array('id'=>$instance->customint5));
+                if (!$cohort) {
+                    return null;
+                }
+                $a = format_string($cohort->name, true, array('context'=>context::instance_by_id($cohort->contextid)));
+                return $OUTPUT->box(markdown_to_html(get_string('cohortnonmemberinfo', 'enrol_self', $a)));
+            }
+        }
+
         require_once("$CFG->dirroot/enrol/self/locallib.php");
         require_once("$CFG->dirroot/group/lib.php");
 
@@ -206,7 +226,7 @@ class enrol_self_plugin extends enrol_plugin {
                 }
 
                 $this->enrol_user($instance, $USER->id, $instance->roleid, $timestart, $timeend);
-                add_to_log($instance->courseid, 'course', 'enrol', '../enrol/users.php?id='.$instance->courseid, $instance->courseid); //there should be userid somewhere!
+                add_to_log($instance->courseid, 'course', 'enrol', '../enrol/users.php?id='.$instance->courseid, $instance->courseid); //TODO: There should be userid somewhere!
 
                 if ($instance->password and $instance->customint1 and $data->enrolpassword !== $instance->password) {
                     // it must be a group enrolment, let's assign group too
@@ -221,7 +241,7 @@ class enrol_self_plugin extends enrol_plugin {
                         }
                     }
                 }
-                // send welcome
+                // Send welcome message.
                 if ($instance->customint4) {
                     $this->email_welcome_message($instance, $USER);
                 }
@@ -237,7 +257,7 @@ class enrol_self_plugin extends enrol_plugin {
 
     /**
      * Add new instance of enrol plugin with default settings.
-     * @param object $course
+     * @param stdClass $course
      * @return int id of new instance
      */
     public function add_default_instance($course) {
@@ -245,6 +265,7 @@ class enrol_self_plugin extends enrol_plugin {
                         'customint2'  => $this->get_config('longtimenosee'),
                         'customint3'  => $this->get_config('maxenrolled'),
                         'customint4'  => $this->get_config('sendcoursewelcomemessage'),
+                        'customint5'  => 0,
                         'enrolperiod' => $this->get_config('enrolperiod', 0),
                         'status'      => $this->get_config('status'),
                         'roleid'      => $this->get_config('roleid', 0));
@@ -257,10 +278,10 @@ class enrol_self_plugin extends enrol_plugin {
     }
 
     /**
-     * Send welcome email to specified user
+     * Send welcome email to specified user.
      *
-     * @param object $instance
-     * @param object $user user record
+     * @param stdClass $instance
+     * @param stdClass $user user record
      * @return void
      */
     protected function email_welcome_message($instance, $user) {
@@ -304,12 +325,12 @@ class enrol_self_plugin extends enrol_plugin {
             $contact = generate_email_supportuser();
         }
 
-        //directly emailing welcome message rather than using messaging
+        // Directly emailing welcome message rather than using messaging.
         email_to_user($user, $contact, $subject, $messagetext, $messagehtml);
     }
 
     /**
-     * Enrol self cron support
+     * Enrol self cron support.
      * @return void
      */
     public function cron() {
@@ -323,10 +344,10 @@ class enrol_self_plugin extends enrol_plugin {
 
         $now = time();
 
-        //note: the logic of self enrolment guarantees that user logged in at least once (=== u.lastaccess set)
-        //      and that user accessed course at least once too (=== user_lastaccess record exists)
+        // Note: the logic of self enrolment guarantees that user logged in at least once (=== u.lastaccess set)
+        //       and that user accessed course at least once too (=== user_lastaccess record exists).
 
-        // first deal with users that did not log in for a really long time
+        // First deal with users that did not log in for a really long time.
         $sql = "SELECT e.*, ue.userid
                   FROM {user_enrolments} ue
                   JOIN {enrol} e ON (e.id = ue.enrolid AND e.enrol = 'self' AND e.customint2 > 0)
@@ -341,7 +362,7 @@ class enrol_self_plugin extends enrol_plugin {
         }
         $rs->close();
 
-        // now unenrol from course user did not visit for a long time
+        // Now unenrol from course user did not visit for a long time.
         $sql = "SELECT e.*, ue.userid
                   FROM {user_enrolments} ue
                   JOIN {enrol} e ON (e.id = ue.enrolid AND e.enrol = 'self' AND e.customint2 > 0)
@@ -360,7 +381,7 @@ class enrol_self_plugin extends enrol_plugin {
     }
 
      /**
-     * Gets an array of the user enrolment actions
+     * Gets an array of the user enrolment actions.
      *
      * @param course_enrolment_manager $manager
      * @param stdClass $ue A user enrolment object
@@ -388,7 +409,7 @@ class enrol_self_plugin extends enrol_plugin {
  * Indicates API features that the enrol plugin supports.
  *
  * @param string $feature
- * @return mixed True if yes (some features may use other values)
+ * @return mixed true if yes (some features may use other values)
  */
 function enrol_self_supports($feature) {
     switch($feature) {
index a189e7d..df78a2c 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
@@ -18,8 +17,7 @@
 /**
  * Self enrol plugin implementation.
  *
- * @package    enrol
- * @subpackage self
+ * @package    enrol_self
  * @copyright  2010 Petr Skoda  {@link http://skodak.org}
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
@@ -33,7 +31,7 @@ class enrol_self_enrol_form extends moodleform {
     protected $toomany = false;
 
     /**
-     * Overriding this function to get unique form id for multiple self enrolments
+     * Overriding this function to get unique form id for multiple self enrolments.
      *
      * @return string form identifier
      */
@@ -54,10 +52,10 @@ class enrol_self_enrol_form extends moodleform {
         $mform->addElement('header', 'selfheader', $heading);
 
         if ($instance->customint3 > 0) {
-            // max enrol limit specified
+            // Max enrol limit specified.
             $count = $DB->count_records('user_enrolments', array('enrolid'=>$instance->id));
             if ($count >= $instance->customint3) {
-                // bad luck, no more self enrolments here
+                // Bad luck, no more self enrolments here.
                 $this->toomany = true;
                 $mform->addElement('static', 'notice', '', get_string('maxenrolledreached', 'enrol_self'));
                 return;
@@ -65,7 +63,7 @@ class enrol_self_enrol_form extends moodleform {
         }
 
         if ($instance->password) {
-            //change the id of self enrolment key input as there can be multiple self enrolment methods
+            // Change the id of self enrolment key input as there can be multiple self enrolment methods.
             $mform->addElement('passwordunmask', 'enrolpassword', get_string('password', 'enrol_self'),
                     array('id' => 'enrolpassword_'.$instance->id));
         } else {
@@ -109,7 +107,7 @@ class enrol_self_enrol_form extends moodleform {
                         }
                     }
                     if (!$found) {
-                        // we can not hint because there are probably multiple passwords
+                        // We can not hint because there are probably multiple passwords.
                         $errors['enrolpassword'] = get_string('passwordinvalid', 'enrol_self');
                     }
 
index bab0909..c8c2f02 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
@@ -18,8 +17,7 @@
 /**
  * Self enrolment plugin settings and presets.
  *
- * @package    enrol
- * @subpackage self
+ * @package    enrol_self
  * @copyright  2010 Petr Skoda  {@link http://skodak.org}
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
index 02670f6..7e7d269 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
@@ -18,8 +17,7 @@
 /**
  * Self enrolment plugin - support for user self unenrolment.
  *
- * @package    enrol
- * @subpackage self
+ * @package    enrol_self
  * @copyright  2010 Petr Skoda  {@link http://skodak.org}
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
@@ -41,7 +39,7 @@ require_login($course);
 
 $plugin = enrol_get_plugin('self');
 
-// security defined inside following function
+// Security defined inside following function.
 if (!$plugin->get_unenrolself_link($instance)) {
     redirect(new moodle_url('/course/view.php', array('id'=>$course->id)));
 }
@@ -51,7 +49,7 @@ $PAGE->set_title($plugin->get_instance_name($instance));
 
 if ($confirm and confirm_sesskey()) {
     $plugin->unenrol_user($instance, $USER->id);
-    add_to_log($course->id, 'course', 'unenrol', '../enrol/users.php?id='.$course->id, $course->id); //there should be userid somewhere!
+    add_to_log($course->id, 'course', 'unenrol', '../enrol/users.php?id='.$course->id, $course->id); //TODO: there should be userid somewhere!
     redirect(new moodle_url('/index.php'));
 }
 
index 73f1cde..1153b05 100644 (file)
 /**
  * Self enrolment plugin version specification.
  *
- * @package    enrol
- * @subpackage self
+ * @package    enrol_self
  * @copyright  2010 Petr Skoda  {@link http://skodak.org}
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
 defined('MOODLE_INTERNAL') || die();
 
-$plugin->version   = 2012061700;        // The current plugin version (Date: YYYYMMDDXX)
-$plugin->requires  = 2012061700;        // Requires this Moodle version
+$plugin->version   = 2012082300;        // The current plugin version (Date: YYYYMMDDXX)
+$plugin->requires  = 2012082300;        // Requires this Moodle version
 $plugin->component = 'enrol_self';      // Full name of the plugin (used for diagnostics)
 $plugin->cron      = 180;
\ No newline at end of file
index 868912d..8fcd760 100644 (file)
@@ -8,6 +8,9 @@ required changes in code:
 * use role_get_name() or role_fix_names() if you need any role names, using role.name
   directly from database is not correct any more
 
+other changes:
+* course enrolment manager now works with disabled plugins too
+
 
 === 2.2 ===
 
index f6001d2..4d8e3ae 100644 (file)
@@ -82,7 +82,7 @@ class core_files_external extends external_api {
         if (empty($fileinfo['contextid'])) {
             $context  = get_system_context();
         } else {
-            $context  = get_context_instance_by_id($fileinfo['contextid']);
+            $context  = context::instance_by_id($fileinfo['contextid']);
         }
         if (empty($fileinfo['component'])) {
             $fileinfo['component'] = null;
@@ -272,7 +272,7 @@ class core_files_external extends external_api {
         }
 
         if (!empty($fileinfo['contextid'])) {
-            $context = get_context_instance_by_id($fileinfo['contextid']);
+            $context = context::instance_by_id($fileinfo['contextid']);
         } else {
             $context = get_system_context();
         }
index cfa14e0..6a648b0 100644 (file)
@@ -965,7 +965,7 @@ class files_tree_viewer implements renderable {
         $this->path = array();
         while ($level) {
             $params = $level->get_params();
-            $context = get_context_instance_by_id($params['contextid']);
+            $context = context::instance_by_id($params['contextid']);
             // $this->context is current context
             if ($context->id != $this->context->id or empty($params['filearea'])) {
                 break;
index 1990064..976b0c5 100644 (file)
@@ -506,6 +506,14 @@ class gradingform_guide_controller extends gradingform_controller {
         $comments = $this->definition->guide_comment;
         $options = $this->get_options();
         $guide = '';
+        if (has_capability('moodle/grade:managegradingforms', $page->context)) {
+            $showdescription = true;
+        } else {
+            $showdescription = $options['showdescriptionstudent'];
+        }
+        if ($showdescription) {
+            $guide .= $output->box($this->get_formatted_description(), 'gradingform_guide-description');
+        }
         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');
index ba8aa5d..a2c6501 100644 (file)
@@ -49,8 +49,5 @@ $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();
index ae97eee..247724d 100644 (file)
@@ -508,6 +508,14 @@ class gradingform_rubric_controller extends gradingform_controller {
         $criteria = $this->definition->rubric_criteria;
         $options = $this->get_options();
         $rubric = '';
+        if (has_capability('moodle/grade:managegradingforms', $page->context)) {
+            $showdescription = true;
+        } else {
+            $showdescription = $options['showdescriptionstudent'];
+        }
+        if ($showdescription) {
+            $rubric .= $output->box($this->get_formatted_description(), 'gradingform_rubric-description');
+        }
         if (has_capability('moodle/grade:managegradingforms', $page->context)) {
             $rubric .= $output->display_rubric_mapping_explained($this->get_min_max_score());
             $rubric .= $output->display_rubric($criteria, $options, self::DISPLAY_PREVIEW, 'rubric');
index 1a71458..babdef4 100644 (file)
@@ -49,8 +49,5 @@ $PAGE->set_heading($title);
 
 echo $OUTPUT->header();
 echo $OUTPUT->heading($title);
-if (!empty($options['showdescriptionstudent'])) {
-    echo $OUTPUT->box($controller->get_formatted_description(), 'gradingform_rubric-description');
-}
 echo $controller->render_preview($PAGE);
 echo $OUTPUT->footer();
index c6e8186..0a3de68 100644 (file)
@@ -223,7 +223,7 @@ class grading_manager {
         global $DB;
 
         $this->areacache = $DB->get_record('grading_areas', array('id' => $areaid), '*', MUST_EXIST);
-        $this->context = get_context_instance_by_id($this->areacache->contextid, MUST_EXIST);
+        $this->context = context::instance_by_id($this->areacache->contextid, MUST_EXIST);
         $this->component = $this->areacache->component;
         $this->area = $this->areacache->areaname;
     }
index 954f91f..f796640 100644 (file)
@@ -57,7 +57,7 @@ if (!is_null($areaid)) {
     if (is_null($contextid) or is_null($component) or is_null($area)) {
         throw new coding_exception('The caller script must identify the gradable area.');
     }
-    $context = get_context_instance_by_id($contextid, MUST_EXIST);
+    $context = context::instance_by_id($contextid, MUST_EXIST);
     $manager = get_grading_manager($context, $component, $area);
 }
 
@@ -233,7 +233,6 @@ if (!empty($method)) {
             $tag = html_writer::tag('span', get_string('statusdraft', 'core_grading'), array('class' => 'status draft'));
         }
         echo $output->heading(s($definition->name) . ' ' . $tag, 3, 'definition-name');
-        echo $output->box($controller->get_formatted_description());
         echo $output->box($controller->render_preview($PAGE), 'definition-preview');
     }
 }
index 7765f82..37629eb 100644 (file)
@@ -2610,6 +2610,7 @@ abstract class grade_helper {
      * @return array
      */
     public static function get_info_letters($courseid) {
+        global $SITE;
         if (self::$letterinfo !== null) {
             return self::$letterinfo;
         }
@@ -2617,9 +2618,15 @@ abstract class grade_helper {
         $canmanage = has_capability('moodle/grade:manage', $context);
         $canmanageletters = has_capability('moodle/grade:manageletters', $context);
         if ($canmanage || $canmanageletters) {
+            // Redirect to system context when report is accessed from admin settings MDL-31633
+            if ($context->instanceid == $SITE->id) {
+                $param = array('edit' => 1);
+            } else {
+                $param = array('edit' => 1,'id' => $context->id);
+            }
             self::$letterinfo = array(
                 'view' => new grade_plugin_info('view', new moodle_url('/grade/edit/letter/index.php', array('id'=>$context->id)), get_string('view')),
-                'edit' => new grade_plugin_info('edit', new moodle_url('/grade/edit/letter/index.php', array('edit'=>1,'id'=>$context->id)), get_string('edit'))
+                'edit' => new grade_plugin_info('edit', new moodle_url('/grade/edit/letter/index.php', $param), get_string('edit'))
             );
         } else {
             self::$letterinfo = false;
index 79ec70d..76ff728 100644 (file)
@@ -1238,7 +1238,7 @@ class block_manager {
 
             $systemcontext = context_system::instance();
             $frontpagecontext = context_course::instance(SITEID);
-            $parentcontext = get_context_instance_by_id($data->bui_parentcontextid);
+            $parentcontext = context::instance_by_id($data->bui_parentcontextid);
 
             // Updating stickiness and contexts.  See MDL-21375 for details.
             if (has_capability('moodle/site:manageblocks', $parentcontext)) { // Check permissions in destination
index c962037..4d6ea82 100644 (file)
@@ -621,16 +621,20 @@ class completion_info {
             debugging('set_module_viewed must be called before header is printed',
                     DEBUG_DEVELOPER);
         }
+
         // Don't do anything if view condition is not turned on
         if ($cm->completionview == COMPLETION_VIEW_NOT_REQUIRED || !$this->is_enabled($cm)) {
             return;
         }
+
         // Get current completion state
-        $data = $this->get_data($cm, $userid);
+        $data = $this->get_data($cm, false, $userid);
+
         // If we already viewed it, don't do anything
         if ($data->viewed == COMPLETION_VIEWED) {
             return;
         }
+
         // OK, change state, save it, and update completion
         $data->viewed = COMPLETION_VIEWED;
         $this->internal_set_data($cm, $data);
index e54cf89..8c01fd2 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8" ?>
-<XMLDB PATH="lib/db" VERSION="20120717" COMMENT="XMLDB file for core Moodle tables"
+<XMLDB PATH="lib/db" VERSION="20120825" COMMENT="XMLDB file for core Moodle tables"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:noNamespaceSchemaLocation="../../lib/xmldb/xmldb.xsd"
 >
         <FIELD NAME="customint1" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false" COMMENT="Custom - general int" PREVIOUS="roleid" NEXT="customint2"/>
         <FIELD NAME="customint2" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false" COMMENT="Custom - general int" PREVIOUS="customint1" NEXT="customint3"/>
         <FIELD NAME="customint3" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false" COMMENT="Custom - general int" PREVIOUS="customint2" NEXT="customint4"/>
-        <FIELD NAME="customint4" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false" COMMENT="Custom - general int" PREVIOUS="customint3" NEXT="customchar1"/>
-        <FIELD NAME="customchar1" TYPE="char" LENGTH="255" NOTNULL="false" SEQUENCE="false" COMMENT="Custom - general short name" PREVIOUS="customint4" NEXT="customchar2"/>
-        <FIELD NAME="customchar2" TYPE="char" LENGTH="255" NOTNULL="false" SEQUENCE="false" COMMENT="Custom - general short name" PREVIOUS="customchar1" NEXT="customdec1"/>
-        <FIELD NAME="customdec1" TYPE="number" LENGTH="12" NOTNULL="false" SEQUENCE="false" DECIMALS="7" COMMENT="Custom - general decimal" PREVIOUS="customchar2" NEXT="customdec2"/>
+        <FIELD NAME="customint4" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false" COMMENT="Custom - general int" PREVIOUS="customint3" NEXT="customint5"/>
+        <FIELD NAME="customint5" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false" COMMENT="Custom - general int" PREVIOUS="customint4" NEXT="customint6"/>
+        <FIELD NAME="customint6" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false" COMMENT="Custom - general int" PREVIOUS="customint5" NEXT="customint7"/>
+        <FIELD NAME="customint7" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false" COMMENT="Custom - general int" PREVIOUS="customint6" NEXT="customint8"/>
+        <FIELD NAME="customint8" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false" COMMENT="Custom - general int" PREVIOUS="customint7" NEXT="customchar1"/>
+        <FIELD NAME="customchar1" TYPE="char" LENGTH="255" NOTNULL="false" SEQUENCE="false" COMMENT="Custom - general short name" PREVIOUS="customint8" NEXT="customchar2"/>
+        <FIELD NAME="customchar2" TYPE="char" LENGTH="255" NOTNULL="false" SEQUENCE="false" COMMENT="Custom - general short name" PREVIOUS="customchar1" NEXT="customchar3"/>
+        <FIELD NAME="customchar3" TYPE="char" LENGTH="1333" NOTNULL="false" SEQUENCE="false" COMMENT="Custom - general short name" PREVIOUS="customchar2" NEXT="customdec1"/>
+        <FIELD NAME="customdec1" TYPE="number" LENGTH="12" NOTNULL="false" SEQUENCE="false" DECIMALS="7" COMMENT="Custom - general decimal" PREVIOUS="customchar3" NEXT="customdec2"/>
         <FIELD NAME="customdec2" TYPE="number" LENGTH="12" NOTNULL="false" SEQUENCE="false" DECIMALS="7" COMMENT="Custom - general decimal" PREVIOUS="customdec1" NEXT="customtext1"/>
         <FIELD NAME="customtext1" TYPE="text" NOTNULL="false" SEQUENCE="false" COMMENT="Custom - general text" PREVIOUS="customdec2" NEXT="customtext2"/>
-        <FIELD NAME="customtext2" TYPE="text" NOTNULL="false" SEQUENCE="false" COMMENT="Custom - general text" PREVIOUS="customtext1" NEXT="timecreated"/>
-        <FIELD NAME="timecreated" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="customtext2" NEXT="timemodified"/>
+        <FIELD NAME="customtext2" TYPE="text" NOTNULL="false" SEQUENCE="false" COMMENT="Custom - general text" PREVIOUS="customtext1" NEXT="customtext3"/>
+        <FIELD NAME="customtext3" TYPE="text" NOTNULL="false" SEQUENCE="false" COMMENT="Custom - general text" PREVIOUS="customtext2" NEXT="customtext4"/>
+        <FIELD NAME="customtext4" TYPE="text" NOTNULL="false" SEQUENCE="false" COMMENT="Custom - general text" PREVIOUS="customtext3" NEXT="timecreated"/>
+        <FIELD NAME="timecreated" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="customtext4" NEXT="timemodified"/>
         <FIELD NAME="timemodified" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="timecreated"/>
       </FIELDS>
       <KEYS>
index d5b9856..58bad7d 100644 (file)
@@ -1119,6 +1119,49 @@ function xmldb_main_upgrade($oldversion) {
         upgrade_main_savepoint(true, 2012081600.01);
     }
 
+    if ($oldversion < 2012082300.01) {
+        // Add more custom enrol fields.
+        $table = new xmldb_table('enrol');
+
+        $field = new xmldb_field('customint5', XMLDB_TYPE_INTEGER, '10', null, null, null, null, 'customint4');
+        if (!$dbman->field_exists($table, $field)) {
+            $dbman->add_field($table, $field);
+        }
+
+        $field = new xmldb_field('customint6', XMLDB_TYPE_INTEGER, '10', null, null, null, null, 'customint5');
+        if (!$dbman->field_exists($table, $field)) {
+            $dbman->add_field($table, $field);
+        }
+
+        $field = new xmldb_field('customint7', XMLDB_TYPE_INTEGER, '10', null, null, null, null, 'customint6');
+        if (!$dbman->field_exists($table, $field)) {
+            $dbman->add_field($table, $field);
+        }
+
+        $field = new xmldb_field('customint8', XMLDB_TYPE_INTEGER, '10', null, null, null, null, 'customint7');
+        if (!$dbman->field_exists($table, $field)) {
+            $dbman->add_field($table, $field);
+        }
+
+        $field = new xmldb_field('customchar3', XMLDB_TYPE_CHAR, '1333', null, null, null, null, 'customchar2');
+        if (!$dbman->field_exists($table, $field)) {
+            $dbman->add_field($table, $field);
+        }
+
+        $field = new xmldb_field('customtext3', XMLDB_TYPE_TEXT, null, null, null, null, null, 'customtext2');
+        if (!$dbman->field_exists($table, $field)) {
+            $dbman->add_field($table, $field);
+        }
+
+        $field = new xmldb_field('customtext4', XMLDB_TYPE_TEXT, null, null, null, null, null, 'customtext3');
+        if (!$dbman->field_exists($table, $field)) {
+            $dbman->add_field($table, $field);
+        }
+
+        // Main savepoint reached.
+        upgrade_main_savepoint(true, 2012082300.01);
+    }
+
 
     return true;
 }
index 8e599d6..b145f99 100644 (file)
@@ -26,12 +26,205 @@ defined('MOODLE_INTERNAL') || die();
 
 require_once("$CFG->libdir/pluginlib.php");
 
+
 /**
  * Editor subplugin info class.
+ *
+ * @package   editor_tinymce
+ * @copyright 2012 Petr Skoda {@link http://skodak.org}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 class plugininfo_tinymce extends plugininfo_base {
-
     public function get_uninstall_url() {
         return new moodle_url('/lib/editor/tinymce/subplugins.php', array('delete' => $this->name, 'sesskey' => sesskey()));
     }
+
+    public function get_settings_url() {
+        global $CFG;
+        if (file_exists("$CFG->dirroot/lib/editor/tinymce/plugins/$this->name/settings.php")) {
+            return new moodle_url('/admin/settings.php', array('section'=>'tinymce'.$this->name.'settings'));
+        } else {
+            return null;
+        }
+    }
+
+    public function is_enabled() {
+        static $disabledsubplugins = null; // TODO: MDL-34344 remove this once get_config() is cached via MUC!
+
+        if (is_null($disabledsubplugins)) {
+            $disabledsubplugins = array();
+            $config = get_config('editor_tinymce', 'disabledsubplugins');
+            if ($config) {
+                $config = explode(',', $config);
+                foreach ($config as $sp) {
+                    $sp = trim($sp);
+                    if ($sp !== '') {
+                        $disabledsubplugins[$sp] = $sp;
+                    }
+                }
+            }
+        }
+
+        return !isset($disabledsubplugins[$this->name]);
+    }
+}
+
+
+/**
+ * Special class for TinyMCE subplugin administration.
+ *
+ * @package   editor_tinymce
+ * @copyright 2012 Petr Skoda {@link http://skodak.org}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class tiynce_subplugins_settings extends admin_setting {
+    public function __construct() {
+        $this->nosave = true;
+        parent::__construct('tinymcesubplugins', get_string('subplugintype_tinymce_plural', 'editor_tinymce'), '', '');
+    }
+
+    /**
+     * Always returns true, does nothing.
+     *
+     * @return true
+     */
+    public function get_setting() {
+        return true;
+    }
+
+    /**
+     * Always returns true, does nothing.
+     *
+     * @return true
+     */
+    public function get_defaultsetting() {
+        return true;
+    }
+
+    /**
+     * Always returns '', does not write anything.
+     *
+     * @param string $data
+     * @return string Always returns ''
+     */
+    public function write_setting($data) {
+        // Do not write any setting.
+        return '';
+    }
+
+    /**
+     * Checks if $query is one of the available subplugins.
+     *
+     * @param string $query The string to search for
+     * @return bool Returns true if found, false if not
+     */
+    public function is_related($query) {
+        if (parent::is_related($query)) {
+            return true;
+        }
+
+        $subplugins = get_plugin_list('tinymce');
+        foreach ($subplugins as $name=>$dir) {
+            if (stripos($name, $query) !== false) {
+                return true;
+            }
+
+            $namestr = get_string('pluginname', 'tinymce_'.$name);
+            if (strpos(textlib::strtolower($namestr), textlib::strtolower($query)) !== false) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Builds the XHTML to display the control.
+     *
+     * @param string $data Unused
+     * @param string $query
+     * @return string
+     */
+    public function output_html($data, $query='') {
+        global $CFG, $OUTPUT;
+        require_once("$CFG->libdir/editorlib.php");
+        require_once("$CFG->libdir/pluginlib.php");
+        require_once(__DIR__.'/lib.php');
+        $tinymce = new tinymce_texteditor();
+        $pluginmanager = plugin_manager::instance();
+
+        // display strings
+        $strbuttons = get_string('availablebuttons', 'editor_tinymce');
+        $strdisable = get_string('disable');
+        $strenable = get_string('enable');
+        $strname = get_string('name');
+        $strsettings = get_string('settings');
+        $struninstall = get_string('uninstallplugin', 'admin');
+        $strversion = get_string('version');
+
+        $subplugins = get_plugin_list('tinymce');
+
+        $return = $OUTPUT->heading(get_string('subplugintype_tinymce_plural', 'editor_tinymce'), 3, 'main', true);
+        $return .= $OUTPUT->box_start('generalbox tinymcesubplugins');
+
+        $table = new html_table();
+        $table->head  = array($strname, $strbuttons, $strversion, $strenable, $strsettings, $struninstall);
+        $table->align = array('left', 'left', 'center', 'center', 'center', 'center');
+        $table->data  = array();
+        $table->width = '100%';
+
+        // Iterate through subplugins.
+        foreach ($subplugins as $name => $dir) {
+            $namestr = get_string('pluginname', 'tinymce_'.$name);
+            $version = get_config('tinymce_'.$name, 'version');
+            if ($version === false) {
+                $version = '';
+            }
+            $plugin = $tinymce->get_plugin($name);
+            $plugininfo = $pluginmanager->get_plugin_info('tinymce_'.$name);
+
+            // Add hide/show link.
+            if (!$version) {
+                $hideshow = '';
+                $displayname = html_writer::tag('span', $name, array('class'=>'error'));
+            } else if ($plugininfo->is_enabled()) {
+                $url = new moodle_url('/lib/editor/tinymce/subplugins.php', array('sesskey'=>sesskey(), 'return'=>'settings', 'disable'=>$name));
+                $hideshow = html_writer::empty_tag('img', array('src'=>$OUTPUT->pix_url('i/hide'), 'class'=>'icon', 'alt'=>$strdisable));
+                $hideshow = html_writer::link($url, $hideshow);
+                $displayname = html_writer::tag('span', $namestr);
+            } else {
+                $url = new moodle_url('/lib/editor/tinymce/subplugins.php', array('sesskey'=>sesskey(), 'return'=>'settings', 'enable'=>$name));
+                $hideshow = html_writer::empty_tag('img', array('src'=>$OUTPUT->pix_url('i/show'), 'class'=>'icon', 'alt'=>$strenable));
+                $hideshow = html_writer::link($url, $hideshow);
+                $displayname = html_writer::tag('span', $namestr, array('class'=>'dimmed_text'));
+            }
+
+            // Add available buttons.
+            $buttons = implode(', ', $plugin->get_buttons());
+            $buttons = html_writer::tag('span', $buttons, array('class'=>'tinymcebuttons'));
+
+            // Add settings link.
+            if (!$version) {
+                $settings = '';
+            } else if ($url = $plugininfo->get_settings_url()) {
+                $settings = html_writer::link($url, $strsettings);
+            } else {
+                $settings = '';
+            }
+
+            // Add uninstall info.
+            if ($version) {
+                $url = new moodle_url($plugininfo->get_uninstall_url(), array('return'=>'settings'));
+                $uninstall = html_writer::link($url, $struninstall);
+            } else {
+                $uninstall = '';
+            }
+
+            // Add a row to the table.
+            $table->data[] = array($displayname, $buttons, $version, $hideshow, $settings, $uninstall);
+        }
+        $return .= html_writer::table($table);
+        $return .= html_writer::tag('p', get_string('tablenosave', 'admin'));
+        $return .= $OUTPUT->box_end();
+        return highlight($query, $return);
+    }
 }
index 9179031..305f827 100644 (file)
@@ -37,6 +37,12 @@ abstract class editor_tinymce_plugin {
     /** @var string Plugin folder */
     protected $plugin;
 
+    /** @var array Plugin settings */
+    protected $config = null;
+
+    /** @var array list of buttons defined by this plugin */
+    protected $buttons = array();
+
     /**
      * @param string $plugin Name of folder
      */
@@ -44,6 +50,64 @@ abstract class editor_tinymce_plugin {
         $this->plugin = $plugin;
     }
 
+    /**
+     * Returns list of buttons defined by this plugin.
+     * useful mostly as information when setting custom toolbar.
+     *
+     * @return array
+     */
+    public function get_buttons() {
+        return $this->buttons;
+    }
+    /**
+     * Makes sure config is loaded and cached.
+     * @return void
+     */
+    protected function load_config() {
+        if (!isset($this->config)) {
+            $name = $this->get_name();
+            $this->config = get_config("tinymce_$name");
+        }
+    }
+
+    /**
+     * Returns plugin config value.
+     * @param  string $name
+     * @param  string $default value if config does not exist yet
+     * @return string value or default
+     */
+    public function get_config($name, $default = null) {
+        $this->load_config();
+        return isset($this->config->$name) ? $this->config->$name : $default;
+    }
+
+    /**
+     * Sets plugin config value.
+     * @param  string $name name of config
+     * @param  string $value string config value, null means delete
+     * @return string value
+     */
+    public function set_config($name, $value) {
+        $pluginname = $this->get_name();
+        $this->load_config();
+        if ($value === null) {
+            unset($this->config->$name);
+        } else {
+            $this->config->$name = $value;
+        }
+        set_config($name, $value, "tinymce_$pluginname");
+    }
+
+    /**
+     * Returns name of this tinymce plugin.
+     * @return string
+     */
+    public function get_name() {
+        // All class names start with "tinymce_".
+        $words = explode('_', get_class($this), 2);
+        return $words[1];
+    }
+
     /**
      * Adjusts TinyMCE init parameters for this plugin.
      *
@@ -274,9 +338,23 @@ abstract class editor_tinymce_plugin {
         // Get list of plugin directories.
         $plugins = get_plugin_list('tinymce');
 
+        // Get list of disabled subplugins.
+        $disabled = array();
+        if ($params['moodle_config']->disabledsubplugins) {
+            foreach (explode(',', $params['moodle_config']->disabledsubplugins) as $sp) {
+                $sp = trim($sp);
+                if ($sp !== '') {
+                    $disabled[$sp] = $sp;
+                }
+            }
+        }
+
         // Construct all the plugins.
         $pluginobjects = array();
         foreach ($plugins as $plugin => $dir) {
+            if (isset($disabled[$plugin])) {
+                continue;
+            }
             require_once($dir . '/lib.php');
             $classname = 'tinymce_' . $plugin;
             $pluginobjects[] = new $classname($plugin);
index c52236f..d08d0f1 100644 (file)
 
 
 //== Custom Moodle strings that are not part of upstream TinyMCE ==
+$string['availablebuttons'] = 'Available buttons';
 $string['common:browseimage'] = 'Find or upload an image...';
 $string['common:browsemedia'] = 'Find or upload a sound, video or applet...';
+$string['customtoolbar'] = 'Custom editor toolbar';
+$string['customtoolbar_desc'] = 'Each line contains a list of comma separated button names, use "|" as a group separator. Leave empty if you want standard toolbar. See <a href="{$a}" target="_blank">{$a}</a> for the list of default TinyMCE buttons.';
 $string['fontselectlist'] = 'Available fonts list';
 $string['media_dlg:filename'] = 'Filename';
 $string['pluginname'] = 'TinyMCE HTML editor';
+$string['settings'] = 'General settings';
 $string['subplugindeleteconfirm'] = 'You are about to completely delete TinyMCE subplugin \'{$a}\'. This will completely delete everything in the database associated with this subplugin. Are you SURE you want to continue?';
+$string['subplugintype_tinymce_plural'] = 'Plugins';
 
 
 // == TinyMCE upstream lang strings from all standard upstream plugins ==
index 2ef3db1..d079dae 100644 (file)
@@ -122,6 +122,9 @@ class tinymce_texteditor extends texteditor {
         $context = empty($options['context']) ? context_system::instance() : $options['context'];
 
         $config = get_config('editor_tinymce');
+        if (!isset($config->disabledsubplugins)) {
+            $config->disabledsubplugins = '';
+        }
 
         $fontselectlist = empty($config->fontselectlist) ? '' : $config->fontselectlist;
         $fontbutton = ($fontselectlist === '') ? '' : 'fontselect,';
@@ -177,11 +180,6 @@ class tinymce_texteditor extends texteditor {
         $params['extended_valid_elements'] = 'nolink,tex,algebra,lang[lang]';
         $params['custom_elements'] = 'nolink,~tex,~algebra,lang';
 
-        if (empty($options['legacy'])) {
-            if (isset($options['maxfiles']) and $options['maxfiles'] != 0) {
-                $params['file_browser_callback'] = "M.editor_tinymce.filepicker";
-            }
-        }
         //Add onblur event for client side text validation
         if (!empty($options['required'])) {
             $params['init_instance_callback'] = 'M.editor_tinymce.onblur_event';
@@ -190,12 +188,52 @@ class tinymce_texteditor extends texteditor {
         // Allow plugins to adjust parameters.
         editor_tinymce_plugin::all_update_init_params($params, $context, $options);
 
+        // Should we override the default toolbar layout unconditionally?
+        $customtoolbar = self::parse_toolbar_setting($config->customtoolbar);
+        if ($customtoolbar) {
+            unset($params['theme_advanced_buttons1']);
+            unset($params['theme_advanced_buttons2']);
+            unset($params['theme_advanced_buttons3']);
+            unset($params['theme_advanced_buttons4']);
+            $i = 1;
+            foreach ($customtoolbar as $line) {
+                $params['theme_advanced_buttons'.$i] = $line;
+                $i++;
+            }
+        }
+
         // Remove temporary parameters.
         unset($params['moodle_config']);
 
         return $params;
     }
 
+    /**
+     * Parse the custom toolbar setting.
+     * @param string $customtoolbar
+     * @return array csv toolbar lines
+     */
+    public static function parse_toolbar_setting($customtoolbar) {
+        $result = array();
+        $customtoolbar = trim($customtoolbar);
+        if ($customtoolbar === '') {
+            return $result;
+        }
+        $customtoolbar = str_replace("\r", "\n", $customtoolbar);
+        $customtoolbar = strtolower($customtoolbar);
+        foreach (explode("\n", $customtoolbar) as $line) {
+            $line = preg_replace('/[^a-z0-9_,\|\-]/', ',', $line);
+            $line = str_replace('|', ',|,', $line);
+            $line = preg_replace('/,,+/', ',', $line);
+            $line = trim($line, ',|');
+            if ($line === '') {
+                continue;
+            }
+            $result[] = $line;
+        }
+        return $result;
+    }
+
     /**
      * Gets a named plugin object. Will cause fatal error if plugin doesn't
      * exist. This is intended for use by plugin files themselves.
index 6532313..1cacacf 100644 (file)
@@ -24,6 +24,9 @@ defined('MOODLE_INTERNAL') || die();
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 class tinymce_dragmath extends editor_tinymce_plugin {
+    /** @var array list of buttons defined by this plugin */
+    protected $buttons = array('dragmath');
+
     protected function update_init_params(array &$params, context $context,
             array $options = null) {
 
index 09f23c5..910cd56 100644 (file)
@@ -24,6 +24,9 @@ defined('MOODLE_INTERNAL') || die();
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 class tinymce_moodleemoticon extends editor_tinymce_plugin {
+    /** @var array list of buttons defined by this plugin */
+    protected $buttons = array('moodleemoticon');
+
     protected function update_init_params(array &$params, context $context,
             array $options = null) {
         global $OUTPUT;
index afec343..f284b7a 100644 (file)
@@ -24,9 +24,19 @@ defined('MOODLE_INTERNAL') || die();
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 class tinymce_moodleimage extends editor_tinymce_plugin {
+    /** @var array list of buttons defined by this plugin */
+    protected $buttons = array('image');
+
     protected function update_init_params(array &$params, context $context,
             array $options = null) {
 
+        // Add file picker callback.
+        if (empty($options['legacy'])) {
+            if (isset($options['maxfiles']) and $options['maxfiles'] != 0) {
+                $params['file_browser_callback'] = "M.editor_tinymce.filepicker";
+            }
+        }
+
         // This plugin overrides standard 'image' button, no need to insert new button.
 
         // Add JS file, which uses default name.
index 5669f47..048b1f5 100644 (file)
@@ -24,9 +24,19 @@ defined('MOODLE_INTERNAL') || die();
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 class tinymce_moodlemedia extends editor_tinymce_plugin {
+    /** @var array list of buttons defined by this plugin */
+    protected $buttons = array('moodlemedia');
+
     protected function update_init_params(array &$params, context $context,
             array $options = null) {
 
+        // Add file picker callback.
+        if (empty($options['legacy'])) {
+            if (isset($options['maxfiles']) and $options['maxfiles'] != 0) {
+                $params['file_browser_callback'] = "M.editor_tinymce.filepicker";
+            }
+        }
+
         // Add button after emoticon button in advancedbuttons3.
         $added = $this->add_button_after($params, 3, 'moodlemedia', 'moodleemoticon', false);
 
index fb12c77..fedb3cc 100644 (file)
@@ -24,6 +24,9 @@ defined('MOODLE_INTERNAL') || die();
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 class tinymce_moodlenolink extends editor_tinymce_plugin {
+    /** @var array list of buttons defined by this plugin */
+    protected $buttons = array('moodlenolink');
+
     protected function update_init_params(array &$params, context $context,
             array $options = null) {
 
index 6e36f41..4f1ac23 100644 (file)
@@ -27,8 +27,11 @@ require('../../../../../config.php');
 @error_reporting(E_ALL ^ E_NOTICE); // Hide notices even if Moodle is configured to show them.
 
 // General settings
-$config['general.engine'] = get_config('editor_tinymce', 'spellengine') ?
-        get_config('editor_tinymce', 'spellengine') : 'GoogleSpell';
+$engine = get_config('tinymce_spellchecker', 'spellengine');
+if (!$engine) {
+    $engine = 'GoogleSpell';
+}
+$config['general.engine'] = $engine;
 
 // GoogleSpell settings
 $config['GoogleSpell.proxyhost'] = isset($CFG->proxyhost) ? $CFG->proxyhost : '';
diff --git a/lib/editor/tinymce/plugins/spellchecker/db/install.php b/lib/editor/tinymce/plugins/spellchecker/db/install.php
new file mode 100644 (file)
index 0000000..dc659b5
--- /dev/null
@@ -0,0 +1,32 @@
+<?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/>.
+
+/**
+ * Spellchecker post install script.
+ *
+ * @package   tinymce_spellchecker
+ * @copyright 2012 Petr Skoda {@link http://skodak.org}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+function xmldb_tinymce_spellchecker_install() {
+    global $CFG, $DB;
+    require_once(__DIR__.'/upgradelib.php');
+
+    tinymce_spellchecker_migrate_settings();
+}
diff --git a/lib/editor/tinymce/plugins/spellchecker/db/upgrade.php b/lib/editor/tinymce/plugins/spellchecker/db/upgrade.php
new file mode 100644 (file)
index 0000000..4012b1f
--- /dev/null
@@ -0,0 +1,40 @@
+<?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/>.
+
+/**
+ * Spellchecker upgrade script.
+ *
+ * @package   tinymce_spellchecker
+ * @copyright 2012 Petr Skoda {@link http://skodak.org}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+function xmldb_tinymce_spellchecker_upgrade($oldversion) {
+    global $CFG, $DB;
+    require_once(__DIR__.'/upgradelib.php');
+
+    $dbman = $DB->get_manager();
+
+    if ($oldversion < 2012051800) {
+        tinymce_spellchecker_migrate_settings();
+        upgrade_plugin_savepoint(true, 2012051800, 'tinymce', 'spellchecker');
+    }
+
+
+    return true;
+}
diff --git a/lib/editor/tinymce/plugins/spellchecker/db/upgradelib.php b/lib/editor/tinymce/plugins/spellchecker/db/upgradelib.php
new file mode 100644 (file)
index 0000000..0a99c1d
--- /dev/null
@@ -0,0 +1,41 @@
+<?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/>.
+
+/**
+ * Spellchecker upgrade script.
+ *
+ * @package   tinymce_spellchecker
+ * @copyright 2012 Petr Skoda {@link http://skodak.org}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Migrate spell related settings from tinymce.
+ */
+function tinymce_spellchecker_migrate_settings() {
+    $engine = get_config('editor_tinymce', 'spellengine');
+    if ($engine !== false) {
+        set_config('spellengine', $engine, 'tinymce_spellchecker');
+        unset_config('spellengine', 'editor_tinymce');
+    }
+    $list = get_config('editor_tinymce', 'spelllanguagelist');
+    if ($list !== false) {
+        set_config('spelllanguagelist', $list, 'tinymce_spellchecker');
+        unset_config('spelllanguagelist', 'editor_tinymce');
+    }
+}
index 2755b43..b2aee6d 100644 (file)
@@ -25,13 +25,15 @@ defined('MOODLE_INTERNAL') || die();
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 class tinymce_spellchecker extends editor_tinymce_plugin {
+    /** @var array list of buttons defined by this plugin */
+    protected $buttons = array('spellchecker');
+
     protected function update_init_params(array &$params, context $context,
             array $options = null) {
         global $CFG;
 
         // Check at least one language is supported.
-        $config = $params['moodle_config'];
-        $spelllanguagelist = empty($config->spelllanguagelist) ? '' : $config->spelllanguagelist;
+        $spelllanguagelist = $this->get_config('spelllanguagelist', '');
         if ($spelllanguagelist !== '') {
             // Add button after code button in advancedbuttons3.
             $added = $this->add_button_after($params, 3, 'spellchecker', 'code', false);
diff --git a/lib/editor/tinymce/plugins/spellchecker/settings.php b/lib/editor/tinymce/plugins/spellchecker/settings.php
new file mode 100644 (file)
index 0000000..c4081e3
--- /dev/null
@@ -0,0 +1,38 @@
+<?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/>.
+
+/**
+ * Spellchecker settings.
+ *
+ * @package   tinymce_spellchecker
+ * @copyright 2012 Petr Skoda {@link http://skodak.org}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die;
+
+if ($ADMIN->fulltree) {
+    $options = array(
+        'PSpell'=>'PSpell',
+        'GoogleSpell'=>'Google Spell',
+        'PSpellShell'=>'PSpellShell');
+    $settings->add(new admin_setting_configselect('tinymce_spellchecker/spellengine',
+        get_string('spellengine', 'admin'), '', 'GoogleSpell', $options));
+    $settings->add(new admin_setting_configtextarea('tinymce_spellchecker/spelllanguagelist',
+        get_string('spelllanguagelist', 'admin'), '',
+        '+English=en,Danish=da,Dutch=nl,Finnish=fi,French=fr,German=de,Italian=it,Polish=pl,' .
+            'Portuguese=pt,Spanish=es,Swedish=sv', PARAM_RAW));
+}
index 83aac6d..0ba52d0 100644 (file)
@@ -25,7 +25,7 @@
 defined('MOODLE_INTERNAL') || die();
 
 // The current plugin version (Date: YYYYMMDDXX).
-$plugin->version   = 2012051701;
+$plugin->version   = 2012051800;
 // Required Moodle version.
 $plugin->requires  = 2011112900;
 // Full name of the plugin (used for diagnostics).
index 3d973e1..d7ffb88 100644 (file)
 
 defined('MOODLE_INTERNAL') || die;
 
+$ADMIN->add('editorsettings', new admin_category('editortinymce', new lang_string('pluginname', 'editor_tinymce')));
+
+$settings = new admin_settingpage('editorsettingstinymce', new lang_string('settings', 'editor_tinymce'));
 if ($ADMIN->fulltree) {
-    $options = array(
-        'PSpell'=>'PSpell',
-        'GoogleSpell'=>'Google Spell',
-        'PSpellShell'=>'PSpellShell');
-    $settings->add(new admin_setting_configselect('editor_tinymce/spellengine',
-            get_string('spellengine', 'admin'), '', 'GoogleSpell', $options));
-    $settings->add(new admin_setting_configtextarea('editor_tinymce/spelllanguagelist',
-            get_string('spelllanguagelist', 'admin'), '',
-            '+English=en,Danish=da,Dutch=nl,Finnish=fi,French=fr,German=de,Italian=it,Polish=pl,' .
-            'Portuguese=pt,Spanish=es,Swedish=sv', PARAM_RAW));
+    require_once(__DIR__.'/adminlib.php');
+    $settings->add(new tiynce_subplugins_settings());
+    $settings->add(new admin_setting_heading('tinymcegeneralheader', new lang_string('settings'), ''));
+    $settings->add(new admin_setting_configtextarea('editor_tinymce/customtoolbar',
+        get_string('customtoolbar', 'editor_tinymce'), get_string('customtoolbar_desc', 'editor_tinymce', 'http://www.tinymce.com/wiki.php/Buttons/controls'), '', PARAM_RAW, 100, 6));
     $settings->add(new admin_setting_configtextarea('editor_tinymce/fontselectlist',
         get_string('fontselectlist', 'editor_tinymce'), '',
         'Trebuchet=Trebuchet MS,Verdana,Arial,Helvetica,sans-serif;Arial=arial,helvetica,sans-serif;Courier New=courier new,courier,monospace;Georgia=georgia,times new roman,times,serif;Tahoma=tahoma,arial,helvetica,sans-serif;Times New Roman=times new roman,times,serif;Verdana=verdana,arial,helvetica,sans-serif;Impact=impact;Wingdings=wingdings', PARAM_RAW));
 }
+$ADMIN->add('editortinymce', $settings);
+unset($settings);
+
+$subplugins = get_plugin_list('tinymce');
+$disabled = array(); // Disabling of subplugins to be implemented later.
+foreach ($subplugins as $name=>$dir) {
+    if (file_exists("$dir/settings.php")) {
+        $settings = new admin_settingpage('tinymce'.$name.'settings', new lang_string('pluginname', 'tinymce_'.$name), 'moodle/site:config', in_array($name, $disabled));
+        // settings.php may create a subcategory or unset the settings completely.
+        include("$dir/settings.php");
+        if ($settings) {
+            $ADMIN->add('editortinymce', $settings);
+        }
+    }
+}
+unset($subplugins);
+unset($disabled);
+
+// TinyMCE does not have standard settings page.
+$settings = null;
index beba8f4..57c153a 100644 (file)
@@ -27,6 +27,8 @@ require_once($CFG->libdir.'/adminlib.php');
 
 $delete  = optional_param('delete', '', PARAM_PLUGIN);
 $confirm = optional_param('confirm', '', PARAM_BOOL);
+$disable = optional_param('disable', '', PARAM_PLUGIN);
+$enable  = optional_param('enable', '', PARAM_PLUGIN);
 $return  = optional_param('return', 'overview', PARAM_ALPHA);
 
 $PAGE->set_context(context_system::instance());
@@ -69,6 +71,27 @@ if ($delete) {
         echo $OUTPUT->footer();
         die();
     }
+
+} else {
+    $disabled = array();
+    $disabledsubplugins = get_config('editor_tinymce', 'disabledsubplugins');
+    if ($disabledsubplugins) {
+        $disabledsubplugins = explode(',', $disabledsubplugins);
+        foreach ($disabledsubplugins as $sp) {
+            $sp = trim($sp);
+            if ($sp !== '') {
+                $disabled[$sp] = $sp;
+            }
+        }
+    }
+
+    if ($disable) {
+        $disabled[$disable] = $disable;
+    } else if ($enable) {
+        unset($disabled[$enable]);
+    }
+
+    set_config('disabledsubplugins', implode(',', $disabled), 'editor_tinymce');
 }
 
 redirect($returnurl);
diff --git a/lib/editor/tinymce/tests/editor_test.php b/lib/editor/tinymce/tests/editor_test.php
new file mode 100644 (file)
index 0000000..27db61b
--- /dev/null
@@ -0,0 +1,53 @@
+<?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/>.
+
+/**
+ * TinyMCE tests.
+ *
+ * @package   editor_tinymce
+ * @category  phpunit
+ * @copyright 2012 Petr Skoda {@link http://skodak.org}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+
+/**
+ * TinyMCE tests.
+ *
+ * @package   editor_tinymce
+ * @category  phpunit
+ * @copyright 2012 Petr Skoda {@link http://skodak.org}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class editor_tinymce_testcase extends advanced_testcase {
+
+    public function test_toolbar_parsing() {
+        global $CFG;
+        require_once("$CFG->dirroot/lib/editorlib.php");
+        require_once("$CFG->dirroot/lib/editor/tinymce/lib.php");
+
+        $result = tinymce_texteditor::parse_toolbar_setting("bold,italic\npreview");
+        $this->assertSame(array('bold,italic', 'preview'), $result);
+
+        $result = tinymce_texteditor::parse_toolbar_setting("| bold,|italic*blink\rpreview\n\n| \n paste STYLE | ");
+        $this->assertSame(array('bold,|,italic,blink', 'preview', 'paste,style'), $result);
+
+        $result = tinymce_texteditor::parse_toolbar_setting("| \n\n| \n \r");
+        $this->assertSame(array(), $result);
+    }
+}
diff --git a/lib/editor/tinymce/upgrade.txt b/lib/editor/tinymce/upgrade.txt
new file mode 100644 (file)
index 0000000..7374a19
--- /dev/null
@@ -0,0 +1,9 @@
+This files describes API changes in /lib/editor/tinymce/* - TinyMCE editor,
+information provided here is intended especially for developers.
+
+
+=== 2.4 ===
+
+new features:
+
+* subplugin support - see http://docs.moodle.org/dev/TinyMCE_plugins
\ No newline at end of file
index f547850..0c6e44d 100644 (file)
@@ -520,7 +520,7 @@ function external_generate_token($tokentype, $serviceorid, $userid, $contextorid
         $service = $serviceorid;
     }
     if (!is_object($contextorid)){
-        $context = get_context_instance_by_id($contextorid, MUST_EXIST);
+        $context = context::instance_by_id($contextorid, MUST_EXIST);
     } else {
         $context = $contextorid;
     }
index 05b3489..0f0816b 100644 (file)
@@ -401,7 +401,7 @@ class file_info_context_course extends file_info {
     public function get_parent() {
         //TODO: error checking if get_parent_contextid() returns false
         $pcid = get_parent_contextid($this->context);
-        $parent = get_context_instance_by_id($pcid);
+        $parent = context::instance_by_id($pcid, IGNORE_MISSING);
         return $this->browser->get_file_info($parent);
     }
 }
index 1cded83..9cab450 100644 (file)
@@ -195,7 +195,7 @@ class file_info_context_coursecat extends file_info {
      */
     public function get_parent() {
         $cid = get_parent_contextid($this->context);
-        $parent = get_context_instance_by_id($cid);
+        $parent = context::instance_by_id($cid, IGNORE_MISSING);
         return $this->browser->get_file_info($parent);
     }
 }
index 0357ee3..7e8ac77 100644 (file)
@@ -283,7 +283,7 @@ class file_info_context_module extends file_info {
      */
     public function get_parent() {
         $pcid = get_parent_contextid($this->context);
-        $parent = get_context_instance_by_id($pcid);
+        $parent = context::instance_by_id($pcid, IGNORE_MISSING);
         return $this->browser->get_file_info($parent);
     }
 }
index fc24b9b..6c8997c 100644 (file)
@@ -1204,6 +1204,10 @@ function get_fast_modinfo(&$course, $userid=0) {
         debugging('Coding problem - missing course modinfo property in get_fast_modinfo() call');
     }
 
+    if (!property_exists($course, 'sectioncache')) {
+        debugging('Coding problem - missing course sectioncache property in get_fast_modinfo() call');
+    }
+
     unset($cache[$course->id]); // prevent potential reference problems when switching users
 
     $cache[$course->id] = new course_modinfo($course, $userid);
index 43dfe9e..d3c0664 100644 (file)
@@ -1131,7 +1131,13 @@ class global_navigation extends navigation_node {
                     $addedcategories[$category->id] = $categoryparent->add($category->name, $url, self::TYPE_CATEGORY, $category->name, $category->id);
 
                     if (!$category->visible) {
-                        if (!has_capability('moodle/category:viewhiddencategories', context_coursecat::instance($category->parent))) {
+                        // Let's decide the context where viewhidden cap checks will happen.
+                        if ($category->parent == '0') {
+                            $contexttocheck = context_system::instance();
+                        } else {
+                            $contexttocheck = context_coursecat::instance($category->parent);
+                        }
+                        if (!has_capability('moodle/category:viewhiddencategories', $contexttocheck)) {
                             $addedcategories[$category->id]->display = false;
                         } else {
                             $addedcategories[$category->id]->hidden = true;
index 149f351..7dc749a 100644 (file)
@@ -209,39 +209,44 @@ function get_grade_options() {
 }
 
 /**
- * match grade options
- * if no match return error or match nearest
+ * Check whether a given grade is one of a list of allowed options. If not,
+ * depending on $matchgrades, either return the nearest match, or return false
+ * to signal an error.
  * @param array $gradeoptionsfull list of valid options
  * @param int $grade grade to be tested
  * @param string $matchgrades 'error' or 'nearest'
- * @return mixed either 'fixed' value or false if erro
+ * @return mixed either 'fixed' value or false if error.
  */
-function match_grade_options($gradeoptionsfull, $grade, $matchgrades='error') {
+function match_grade_options($gradeoptionsfull, $grade, $matchgrades = 'error') {
+
     if ($matchgrades == 'error') {
-        // if we just need an error...
+        // (Almost) exact match, or an error.
         foreach ($gradeoptionsfull as $value => $option) {
-            // slightly fuzzy test, never check floats for equality :-)
+            // Slightly fuzzy test, never check floats for equality.
             if (abs($grade - $value) < 0.00001) {
-                return $grade;
+                return $value; // Be sure the return the proper value.
             }
         }
-        // didn't find a match so that's an error
+        // Didn't find a match so that's an error.
         return false;
+
     } else if ($matchgrades == 'nearest') {
-        // work out nearest value
-        $hownear = array();
+        // Work out nearest value
+        $best = false;
+        $bestmismatch = 2;
         foreach ($gradeoptionsfull as $value => $option) {
-            if ($grade==$value) {
-                return $grade;
+            $newmismatch = abs($grade - $value);
+            if ($newmismatch < $bestmismatch) {
+                $best = $value;
+                $bestmismatch = $newmismatch;
             }
-            $hownear[ $value ] = abs( $grade - $value );
         }
-        // reverse sort list of deltas and grab the last (smallest)
-        asort( $hownear, SORT_NUMERIC );
-        reset( $hownear );
-        return key( $hownear );
+        return $best;
+
     } else {
-        return false;
+        // Unknow option passed.
+        throw new coding_exception('Unknown $matchgrades ' . $matchgrades .
+                ' passed to match_grade_options');
     }
 }
 
@@ -780,14 +785,22 @@ function question_load_questions($questionids, $extrafields = '', $join = '') {
  */
 function _tidy_question($question, $loadtags = false) {
     global $CFG;
+
+    // Load question-type specific fields.
     if (!question_bank::is_qtype_installed($question->qtype)) {
         $question->questiontext = html_writer::tag('p', get_string('warningmissingtype',
                 'qtype_missingtype')) . $question->questiontext;
     }
     question_bank::get_qtype($question->qtype)->get_question_options($question);
+
+    // Convert numeric fields to float. (Prevents these being displayed as 1.0000000.)
+    $question->defaultmark += 0;
+    $question->penalty += 0;
+
     if (isset($question->_partiallyloaded)) {
         unset($question->_partiallyloaded);
     }
+
     if ($loadtags && !empty($CFG->usetags)) {
         require_once($CFG->dirroot . '/tag/lib.php');
         $question->tags = tag_get_tags_array('question', $question->id);
@@ -1106,7 +1119,7 @@ function question_category_options($contexts, $top = false, $currentcat = 0,
     $categoriesarray = array();
     foreach ($pcontexts as $pcontext) {
         $contextstring = print_context_name(
-                get_context_instance_by_id($pcontext), true, true);
+                context::instance_by_id($pcontext), true, true);
         foreach ($categories as $category) {
             if ($category->contextid == $pcontext) {
                 $cid = $category->id;
@@ -1333,7 +1346,7 @@ function question_has_capability_on($question, $cap, $cachecat = -1) {
         }
     }
     $category = $categories[$question->category];
-    $context = get_context_instance_by_id($category->contextid);
+    $context = context::instance_by_id($category->contextid);
 
     if (array_search($cap, $question_questioncaps)!== false) {
         if (!has_capability('moodle/question:' . $cap . 'all', $context)) {
diff --git a/lib/session-test.php b/lib/session-test.php
deleted file mode 100644 (file)
index f38479f..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-<?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 is a tiny standalone diagnostic script to test that sessions
- * are working correctly on a given server.
- *
- * Just run it from a browser.   The first time you run it will
- * set a new variable, and after that it will try to find it again.
- * The random number is just to prevent browser caching.
- *
- * @todo add code that actually tests moodle sessions, the old one only tested
- *       PHP sessions used from installer, not the real moodle sessions
- * @package   moodlecore
- * @copyright 1999 onwards Martin Dougiamas  {@link http://moodle.com}
- * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-/** Include config {@see config.php} */
-require '../config.php';
-
-$PAGE->set_url('/lib/session-test.php');
-
-error('session test not reimplemented yet'); //DO NOT localize or use print_error()!
-//
-//TODO: add code that actually tests moodle sessions, the old one only tested PHP sessions used from installer, not the real moodle sessions
index 51f8d82..16b2aa3 100644 (file)
@@ -206,7 +206,7 @@ class required_capability_exception extends moodle_exception {
         $capabilityname = get_capability_string($capability);
         if ($context->contextlevel == CONTEXT_MODULE and preg_match('/:view$/', $capability)) {
             // we can not go to mod/xx/view.php because we most probably do not have cap to view it, let's go to course instead
-            $paranetcontext = get_context_instance_by_id(get_parent_contextid($context));
+            $paranetcontext = context::instance_by_id(get_parent_contextid($context));
             $link = get_context_url($paranetcontext);
         } else {
             $link = get_context_url($context);
index 52d700f..4542513 100644 (file)
@@ -2271,7 +2271,7 @@ class accesslib_testcase extends advanced_testcase {
 
         foreach ($DB->get_records('context') as $contextid=>$record) {
             $context = context::instance_by_id($contextid);
-            $this->assertSame(get_context_instance_by_id($contextid), $context);
+            $this->assertSame(context::instance_by_id($contextid, IGNORE_MISSING), $context);
             $this->assertSame(get_context_instance($record->contextlevel, $record->instanceid), $context);
             $this->assertSame(get_parent_contexts($context), $context->get_parent_context_ids());
             if ($context->id == SYSCONTEXTID) {
index 6e81cd7..5092831 100644 (file)
@@ -257,7 +257,7 @@ class completionlib_testcase extends basic_testcase {
             ->will($this->returnValue(true));
         $c->expects($this->at(1))
             ->method('get_data')
-            ->with($cm, 1337)
+            ->with($cm, false, 1337)
             ->will($this->returnValue((object)array('viewed'=>COMPLETION_NOT_VIEWED)));
         $c->expects($this->at(2))
             ->method('internal_set_data')
index 2e5d431..d7d767d 100644 (file)
@@ -55,4 +55,19 @@ class questionlib_testcase extends basic_testcase {
             array(0 => 't1', 1 => 't2', 2 => 't3'));
     }
 
+    public function test_match_grade_options() {
+        $gradeoptions = question_bank::fraction_options_full();
+
+        $this->assertEquals(0.3333333, match_grade_options($gradeoptions, 0.3333333, 'error'));
+        $this->assertEquals(0.3333333, match_grade_options($gradeoptions, 0.333333, 'error'));
+        $this->assertEquals(0.3333333, match_grade_options($gradeoptions, 0.33333, 'error'));
+        $this->assertFalse(match_grade_options($gradeoptions, 0.3333, 'error'));
+
+        $this->assertEquals(0.3333333, match_grade_options($gradeoptions, 0.3333333, 'nearest'));
+        $this->assertEquals(0.3333333, match_grade_options($gradeoptions, 0.333333, 'nearest'));
+        $this->assertEquals(0.3333333, match_grade_options($gradeoptions, 0.33333, 'nearest'));
+        $this->assertEquals(0.3333333, match_grade_options($gradeoptions, 0.33, 'nearest'));
+
+        $this->assertEquals(-0.1428571, match_grade_options($gradeoptions, -0.15, 'nearest'));
+    }
 }
index 1295b00..b3d7339 100644 (file)
@@ -1072,7 +1072,7 @@ function format_text($text, $format = FORMAT_MOODLE, $options = NULL, $courseid_
         if (is_object($options['context'])) {
             $context = $options['context'];
         } else {
-            $context = get_context_instance_by_id($options['context']);
+            $context = context::instance_by_id($options['context']);
         }
     } else if ($courseid_do_not_use) {
         // legacy courseid
@@ -1281,7 +1281,7 @@ function format_string($string, $striplinks = true, $options = NULL) {
         // fallback to $PAGE->context this may be problematic in CLI and other non-standard pages :-(
         $options['context'] = $PAGE->context;
     } else if (is_numeric($options['context'])) {
-        $options['context'] = get_context_instance_by_id($options['context']);
+        $options['context'] = context::instance_by_id($options['context']);
     }
 
     if (!$options['context']) {
index 5d0aca4..4c908db 100644 (file)
@@ -179,7 +179,6 @@ $preferences->blocknoncontacts  =  get_user_preferences( 'message_blocknoncontac
 /// Display page header
 $streditmymessage = get_string('editmymessage', 'message');
 $strparticipants  = get_string('participants');
-$userfullname     = fullname($user, true);
 
 $PAGE->set_title("$course->shortname: $streditmymessage");
 if ($course->id != SITEID) {
index fb5e4ff..40bed66 100644 (file)
@@ -88,7 +88,7 @@ class assign_grading_table extends table_sql implements renderable {
         $params['assignmentid1'] = (int)$this->assignment->get_instance()->id;
         $params['assignmentid2'] = (int)$this->assignment->get_instance()->id;
 
-        $fields = user_picture::fields('u') . ', u.id as userid, u.firstname as firstname, u.lastname as lastname, ';
+        $fields = user_picture::fields('u') . ', u.id as userid, ';
         $fields .= 's.status as status, s.id as submissionid, s.timecreated as firstsubmission, s.timemodified as timesubmitted, ';
         $fields .= 'g.id as gradeid, g.grade as grade, g.timemodified as timemarked, g.timecreated as firstmarked, g.mailed as mailed, g.locked as locked';
         $from = '{user} u LEFT JOIN {assign_submission} s ON u.id = s.userid AND s.assignment = :assignmentid1' .
index 60a3f92..455afd1 100644 (file)
@@ -417,7 +417,7 @@ class assign {
         $update->duedate = $formdata->duedate;
         $update->allowsubmissionsfromdate = $formdata->allowsubmissionsfromdate;
         $update->grade = $formdata->grade;
-        $update->completionsubmit = $formdata->completionsubmit;
+        $update->completionsubmit = !empty($formdata->completionsubmit);
         $returnid = $DB->insert_record('assign', $update);
         $this->instance = $DB->get_record('assign', array('id'=>$returnid), '*', MUST_EXIST);
         // cache the course record
@@ -637,7 +637,7 @@ class assign {
         $update->duedate = $formdata->duedate;
         $update->allowsubmissionsfromdate = $formdata->allowsubmissionsfromdate;
         $update->grade = $formdata->grade;
-        $update->completionsubmit = $formdata->completionsubmit;
+        $update->completionsubmit = !empty($formdata->completionsubmit);
 
         $result = $DB->update_record('assign', $update);
         $this->instance = $DB->get_record('assign', array('id'=>$update->id), '*', MUST_EXIST);
index 75a56f8..337eb7f 100644 (file)
@@ -80,10 +80,9 @@ class restore_choice_activity_structure_step extends restore_activity_structure_
         global $DB;
 
         $data = (object)$data;
-        $oldid = $data->id;
 
         $data->choiceid = $this->get_new_parentid('choice');
-        $data->optionid = $this->get_mappingid('choice_option', $oldid);
+        $data->optionid = $this->get_mappingid('choice_option', $data->optionid);
         $data->userid = $this->get_mappingid('user', $data->userid);
         $data->timemodified = $this->apply_date_offset($data->timemodified);
 
index fa660c0..0cc49b3 100644 (file)
@@ -1325,7 +1325,7 @@ function data_print_template($template, $records, $data, $search='', $page=0, $r
  * @return array an associative array of the user's rating permissions
  */
 function data_rating_permissions($contextid, $component, $ratingarea) {
-    $context = get_context_instance_by_id($contextid, MUST_EXIST);
+    $context = context::instance_by_id($contextid, MUST_EXIST);
     if ($component != 'mod_data' || $ratingarea != 'entry') {
         return null;
     }
index c35b24c..2e27645 100644 (file)
@@ -62,28 +62,26 @@ $string['delete_item'] = 'Delete question';
 $string['delete_old_items'] = 'Delete old items';
 $string['delete_template'] = 'Delete template';
 $string['delete_templates'] = 'Delete template...';
-$string['depending'] = 'depending items';
-$string['depending_help'] = 'Depending items allow you to show items depend on values from other items.<br />
-<strong>Here an build example to use it:</strong><br />
+$string['depending'] = 'Dependencies';
+$string['depending_help'] = 'It is possible to show an item depending on the value of another item.<br />
+<strong>Here is an example.</strong><br />
 <ul>
-<li>First create an item on which value other items depends.</li>
-<li>Next add a pagebreak.</li>
-<li>Next add the items depend on the item-value before<br />
-Choose in the item creation-form the item in the list "depend item" and put the needed value into the textbox "depend value".</li>
+<li>First, create an item on which another item will depend on.</li>
+<li>Next, add a pagebreak.</li>
+<li>Then add the items dependant on the value of the item created before. Choose the item from the list labelled "Dependence item" and write the required value in the textbox labelled "Dependence value".</li>
 </ul>
-<strong>The structure should looks like this:</strong>
+<strong>The item structure should look like this.</strong>
 <ol>
-<li>Item Q: do you have a car? A: yes/no</li>
+<li>Item Q: Do you have a car? A: yes/no</li>
 <li>Pagebreak</li>
-<li>Item Q: what color has your car?<br />
+<li>Item Q: What colour is your car?<br />
 (this item depends on item 1 with value = yes)</li>
-<li>Item Q: why you have not a car?<br />
+<li>Item Q: Why don\'t you have a car?<br />
 (this item depends on item 1 with value = no)</li>
 <li> ... other items</li>
-</ol>
-That is all. Have fun!';
-$string['dependitem'] = 'depend item';
-$string['dependvalue'] = 'depend value';
+</ol>';
+$string['dependitem'] = 'Dependence item';
+$string['dependvalue'] = 'Dependence value';
 $string['description'] = 'Description';
 $string['do_not_analyse_empty_submits'] = 'Do not analyse empty submits';
 $string['dropdown'] = 'Multiple choice - single answer allowed (dropdownlist)';
index 405bc95..b36bcd6 100644 (file)
@@ -3527,7 +3527,7 @@ function forum_print_post($post, $discussion, $forum, &$cm, $course, $ownpost=fa
  * @return array an associative array of the user's rating permissions
  */
 function forum_rating_permissions($contextid, $component, $ratingarea) {
-    $context = get_context_instance_by_id($contextid, MUST_EXIST);
+    $context = context::instance_by_id($contextid, MUST_EXIST);
     if ($component != 'mod_forum' || $ratingarea != 'post') {
         // We don't know about this component/ratingarea so just return null to get the
         // default restrictive permissions.
index 1ddcc56..ab8657a 100644 (file)
@@ -637,7 +637,7 @@ function glossary_rating_permissions($contextid, $component, $ratingarea) {
         // default restrictive permissions.
         return null;
     }
-    $context = get_context_instance_by_id($contextid);
+    $context = context::instance_by_id($contextid);
     return array(
         'view'    => has_capability('mod/glossary:viewrating', $context),
         'viewany' => has_capability('mod/glossary:viewanyrating', $context),
index ef5e633..1fce2b4 100644 (file)
@@ -73,7 +73,7 @@ function module_specific_buttons($cmid, $cmoptions) {
 function module_specific_controls($totalnumber, $recurse, $category, $cmid, $cmoptions) {
     global $OUTPUT;
     $out = '';
-    $catcontext = get_context_instance_by_id($category->contextid);
+    $catcontext = context::instance_by_id($category->contextid);
     if (has_capability('moodle/question:useall', $catcontext)) {
         if ($cmoptions->hasattempts) {
             $disabled = ' disabled="disabled"';
index 44f00d6..987a39b 100644 (file)
@@ -183,7 +183,7 @@ function quiz_add_random_questions($quiz, $addonpage, $categoryid, $number,
         print_error('invalidcategoryid', 'error');
     }
 
-    $catcontext = get_context_instance_by_id($category->contextid);
+    $catcontext = context::instance_by_id($category->contextid);
     require_capability('moodle/question:useall', $catcontext);
 
     // Find existing random questions in this category that are
index c12e7ff..d1821ca 100644 (file)
@@ -3,9 +3,34 @@ This files describes API changes for quiz report plugins.
 Overview of this plugin type at http://docs.moodle.org/dev/Quiz_reports
 
 
-=== earlier versions ===
+=== 2.3 ===
 
-* ... API changes were not documented properly. Sorry. (There weren't many!)
+* Support for the old way of doing cron in a separate cron.php file has been removed.
+Instead, you need a lib.php file inside the plugin with a cron function
+called quiz_myreportname_cron(). The statistics report is an example of how
+it should be done.
+
+* There was a big refactor of the quiz reports, in issues MDL-32300, MDL-32322 and MDL-3030.
+It is difficult to explain the changes. Probably the best way to understand what
+happened is to look at
+    git log mod/quiz/report/overview
+    git log mod/quiz/report/responses
+and so on. Here are some notes on a few of the changes:
+
+The class quiz_attempt_report was renamed to quiz_attempts_report (with an extra s).
+
+Some globally defined constants with the prefix QUIZ_REPORT_ATTEMPTS_ moved into
+the quiz_attempts_report class. Specifically
+
+quiz_attempts_report::ALL_WITH         replaces QUIZ_REPORT_ATTEMPTS_ALL
+quiz_attempts_report::ENROLLED_ALL     replaces QUIZ_REPORT_ATTEMPTS_ALL_STUDENTS
+quiz_attempts_report::ENROLLED_WITH    replaces QUIZ_REPORT_ATTEMPTS_STUDENTS_WITH
+quiz_attempts_report::ENROLLED_WITHOUT replaces QUIZ_REPORT_ATTEMPTS_STUDENTS_WITH_NO
+
+Your if you have a table class, it needs to be renamed like
+quiz_report_myreportname_table -> quiz_myreportname_table. That is, all the
+class names in your plugin should start with the frankenstyle plugin name
+quiz_myreportname.
 
 
 === 2.2 ===
@@ -17,6 +42,6 @@ This replaces the old way of having a separate cron.php file. Also, the cron
 frequency should be defined in version.php, not in the quiz_reports table.
 
 
-=== 2.3 ===
+=== earlier versions ===
 
-* Support for the old way of doing cron in a separate cron.php file has been removed.
+* ... API changes were not documented properly. Sorry. (There weren't many!)
index 9992dde..2d34a6c 100644 (file)
@@ -59,7 +59,7 @@ class mod_wiki_edit_form extends moodleform {
 
         if (isset($this->_customdata['pagetitle'])) {
             // Page title must be formatted properly here as this is output and not an element.
-            $pagetitle = get_string('editingpage', 'wiki', format_string($this->_customdata['pagetitle'], true, array('context' => get_context_instance_by_id($contextid, MUST_EXIST))));
+            $pagetitle = get_string('editingpage', 'wiki', format_string($this->_customdata['pagetitle'], true, array('context' => context::instance_by_id($contextid, MUST_EXIST))));
         } else {
             $pagetitle = get_string('editing', 'wiki');
         }
index fa262cd..02e6224 100644 (file)
@@ -60,7 +60,7 @@ if ($cmid) {
 }
 
 // Check permissions.
-$categorycontext = get_context_instance_by_id($category->contextid);
+$categorycontext = context::instance_by_id($category->contextid);
 require_capability('moodle/question:add', $categorycontext);
 
 // Ensure other optional params get passed on to question.php.
index 5ad3de9..b208a46 100644 (file)
@@ -71,7 +71,7 @@ if ($param->delete && ($questionstomove = $DB->count_records("question", array("
     if (!$category = $DB->get_record("question_categories", array("id" => $param->delete))) {  // security
         print_error('nocate', 'question', $thispageurl->out(), $param->delete);
     }
-    $categorycontext = get_context_instance_by_id($category->contextid);
+    $categorycontext = context::instance_by_id($category->contextid);
     $qcobject->moveform = new question_move_form($thispageurl,
                 array('contexts'=>array($categorycontext), 'currentcat'=>$param->delete));
     if ($qcobject->moveform->is_cancelled()){
index fd65dba..79339f2 100644 (file)
@@ -246,7 +246,7 @@ class question_category_object {
             $listhtml = $list->to_html(0, array('str'=>$this->str));
             if ($listhtml){
                 echo $OUTPUT->box_start('boxwidthwide boxaligncenter generalbox questioncategories contextlevel' . $list->context->contextlevel);
-                echo $OUTPUT->heading(get_string('questioncatsfor', 'question', print_context_name(get_context_instance_by_id($context))), 3);
+                echo $OUTPUT->heading(get_string('questioncatsfor', 'question', print_context_name(context::instance_by_id($context))), 3);
                 echo $listhtml;
                 echo $OUTPUT->box_end();
             }
@@ -374,7 +374,7 @@ class question_category_object {
         }
         list($parentid, $contextid) = explode(',', $newparent);
         //moodle_form makes sure select element output is legal no need for further cleaning
-        require_capability('moodle/question:managecategory', get_context_instance_by_id($contextid));
+        require_capability('moodle/question:managecategory', context::instance_by_id($contextid));
 
         if ($parentid) {
             if(!($DB->get_field('question_categories', 'contextid', array('id' => $parentid)) == $contextid)) {
@@ -418,12 +418,12 @@ class question_category_object {
         }
 
         // Check permissions.
-        $fromcontext = get_context_instance_by_id($oldcat->contextid);
+        $fromcontext = context::instance_by_id($oldcat->contextid);
         require_capability('moodle/question:managecategory', $fromcontext);
 
         // If moving to another context, check permissions some more.
         if ($oldcat->contextid != $tocontextid) {
-            $tocontext = get_context_instance_by_id($tocontextid);
+            $tocontext = context::instance_by_id($tocontextid);
             require_capability('moodle/question:managecategory', $tocontext);
         }
 
index f28b9c2..bb563f7 100644 (file)
@@ -117,7 +117,7 @@ function question_can_delete_cat($todelete) {
         print_error('cannotdeletecate', 'question');
     } else {
         $contextid = $DB->get_field('question_categories', 'contextid', array('id' => $todelete));
-        require_capability('moodle/question:managecategory', get_context_instance_by_id($contextid));
+        require_capability('moodle/question:managecategory', context::instance_by_id($contextid));
     }
 }
 
@@ -1323,7 +1323,7 @@ class question_bank_view {
         $strdelete = get_string('delete');
 
         list($categoryid, $contextid) = explode(',', $categoryandcontext);
-        $catcontext = get_context_instance_by_id($contextid);
+        $catcontext = context::instance_by_id($contextid);
 
         $canadd = has_capability('moodle/question:add', $catcontext);
         $caneditall =has_capability('moodle/question:editall', $catcontext);
@@ -1467,7 +1467,7 @@ class question_bank_view {
             if (! $tocategory = $DB->get_record('question_categories', array('id' => $tocategoryid, 'contextid' => $contextid))) {
                 print_error('cannotfindcate', 'question');
             }
-            $tocontext = get_context_instance_by_id($contextid);
+            $tocontext = context::instance_by_id($contextid);
             require_capability('moodle/question:add', $tocontext);
             $rawdata = (array) data_submitted();
             $questionids = array();
@@ -1717,7 +1717,7 @@ function question_get_display_preference($param, $default, $type, $thispageurl)
 function require_login_in_context($contextorid = null){
     global $DB, $CFG;
     if (!is_object($contextorid)){
-        $context = get_context_instance_by_id($contextorid);
+        $context = context::instance_by_id($contextorid, IGNORE_MISSING);
     } else {
         $context = $contextorid;
     }
index e4de197..22bc3b8 100644 (file)
@@ -708,7 +708,7 @@ class question_usage_by_activity {
         }
 
         $quba = new question_usage_by_activity($record->component,
-            get_context_instance_by_id($record->contextid));
+            context::instance_by_id($record->contextid, IGNORE_MISSING));
         $quba->set_id_from_database($record->qubaid);
         $quba->set_preferred_behaviour($record->preferredbehaviour);
 
index e1cc7db..7748b6f 100644 (file)
@@ -128,7 +128,7 @@ class qformat_default {
             debugging('You shouldn\'t call setCategory after setQuestions');
         }
         $this->category = $category;
-        $this->importcontext = get_context_instance_by_id($this->category->contextid);
+        $this->importcontext = context::instance_by_id($this->category->contextid);
     }
 
     /**
@@ -408,20 +408,44 @@ class qformat_default {
             $question->timecreated = time();
             $question->modifiedby = $USER->id;
             $question->timemodified = time();
+            $fileoptions = array(
+                    'subdirs' => false,
+                    'maxfiles' => -1,
+                    'maxbytes' => 0,
+                );
+            if (is_array($question->questiontext)) {
+                // Importing images from draftfile.
+                $questiontext = $question->questiontext;
+                $question->questiontext = $questiontext['text'];
+            }
+            if (is_array($question->generalfeedback)) {
+                $generalfeedback = $question->generalfeedback;
+                $question->generalfeedback = $generalfeedback['text'];
+            }
 
             $question->id = $DB->insert_record('question', $question);
-            if (isset($question->questiontextfiles)) {
+
+            if (!empty($questiontext['itemid'])) {
+                $question->questiontext = file_save_draft_area_files($questiontext['itemid'],
+                        $this->importcontext->id, 'question', 'questiontext', $question->id,
+                        $fileoptions, $question->questiontext);
+            } else if (isset($question->questiontextfiles)) {
                 foreach ($question->questiontextfiles as $file) {
                     question_bank::get_qtype($question->qtype)->import_file(
                             $this->importcontext, 'question', 'questiontext', $question->id, $file);
                 }
             }
-            if (isset($question->generalfeedbackfiles)) {
+            if (!empty($generalfeedback['itemid'])) {
+                $question->generalfeedback = file_save_draft_area_files($generalfeedback['itemid'],
+                        $this->importcontext->id, 'question', 'generalfeedback', $question->id,
+                        $fileoptions, $question->generalfeedback);
+            } else if (isset($question->generalfeedbackfiles)) {
                 foreach ($question->generalfeedbackfiles as $file) {
                     question_bank::get_qtype($question->qtype)->import_file(
                             $this->importcontext, 'question', 'generalfeedback', $question->id, $file);
                 }
             }
+            $DB->update_record('question', $question);
 
             $this->questionids[] = $question->id;
 
@@ -500,10 +524,10 @@ class qformat_default {
         }
 
         if ($this->contextfromfile && $contextid !== false) {
-            $context = get_context_instance_by_id($contextid);
+            $context = context::instance_by_id($contextid);
             require_capability('moodle/question:add', $context);
         } else {
-            $context = get_context_instance_by_id($this->category->contextid);
+            $context = context::instance_by_id($this->category->contextid);
         }
         $this->importcontext = $context;
 
@@ -636,6 +660,24 @@ class qformat_default {
         return $question;
     }
 
+    /**
+     * Add a blank combined feedback to a question object.
+     * @param object question
+     * @return object question
+     */
+    protected function add_blank_combined_feedback($question) {
+        $question->correctfeedback['text'] = '';
+        $question->correctfeedback['format'] = $question->questiontextformat;
+        $question->correctfeedback['files'] = array();
+        $question->partiallycorrectfeedback['text'] = '';
+        $question->partiallycorrectfeedback['format'] = $question->questiontextformat;
+        $question->partiallycorrectfeedback['files'] = array();
+        $question->incorrectfeedback['text'] = '';
+        $question->incorrectfeedback['format'] = $question->questiontextformat;
+        $question->incorrectfeedback['files'] = array();
+        return $question;
+    }
+
     /**
      * Given the data known to define a question in
      * this format, this function converts it into a question
@@ -901,6 +943,28 @@ class qformat_default {
 
 class qformat_based_on_xml extends qformat_default {
 
+    /**
+     * A lot of imported files contain unwanted entities.
+     * This method tries to clean up all known problems.
+     * @param string str string to correct
+     * @return string the corrected string
+     */
+    public function cleaninput($str) {
+
+        $html_code_list = array(
+            "&#039;" => "'",
+            "&#8217;" => "'",
+            "&#8220;" => "\"",
+            "&#8221;" => "\"",
+            "&#8211;" => "-",
+            "&#8212;" => "-",
+        );
+        $str = strtr($str, $html_code_list);
+        // Use textlib entities_to_utf8 function to convert only numerical entities.
+        $str = textlib::entities_to_utf8($str, false);
+        return $str;
+    }
+
     /**
      * Return the array moodle is expecting
      * for an HTML text. No processing is done on $text.
index 88e0130..1e7fa0d 100644 (file)
@@ -59,33 +59,6 @@ class qformat_blackboard extends qformat_based_on_xml {
         return mimeinfo('type', '.dat');
     }
 
-    /**
-     * Some softwares put entities in exported files.
-     * This method try to clean up known problems.
-     * @param string str string to correct
-     * @return string the corrected string
-     */
-    public function cleaninput($str) {
-        if (!$this->ishtml) {
-            return $str;
-        }
-        $html_code_list = array(
-            "&#039;" => "'",
-            "&#8217;" => "'",
-            "&#091;" => "[",
-            "&#8220;" => "\"",
-            "&#8221;" => "\"",
-            "&#093;" => "]",
-            "&#039;" => "'",
-            "&#8211;" => "-",
-            "&#8212;" => "-",
-        );
-        $str = strtr($str, $html_code_list);
-        // Use textlib entities_to_utf8 function to convert only numerical entities.
-        $str = textlib::entities_to_utf8($str, false);
-        return $str;
-    }
-
     /**
      * Parse the array of lines into an array of questions
      * this *could* burn memory - but it won't happen that much
index 047bfa9..32652d1 100644 (file)
 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
 /**
- * Blackboard 6.0 question importer.
+ * Blackboard V5 and V6 question importer.
  *
- * @package    qformat
- * @subpackage blackboard_six
+ * @package    qformat_blackboard_six
  * @copyright  2005 Michael Penney
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
-
 defined('MOODLE_INTERNAL') || die();
 
-require_once ($CFG->libdir . '/xmlize.php');
-
-
-/**
- * Blackboard 6.0 question importer.
- *
- * @copyright  2005 Michael Penney
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-class qformat_blackboard_six extends qformat_default {
-    function provide_import() {
-        return true;
-    }
-
-    public function can_import_file($file) {
-        $mimetypes = array(
-            mimeinfo('type', '.dat'),
-            mimeinfo('type', '.zip')
-        );
-        return in_array($file->get_mimetype(), $mimetypes);
-    }
-
-
-    //Function to check and create the needed dir to unzip file to
-    function check_and_create_import_dir($unique_code) {
-
-        global $CFG;
-
-        $status = $this->check_dir_exists($CFG->tempdir."",true);
-        if ($status) {
-            $status = $this->check_dir_exists($CFG->tempdir."/bbquiz_import",true);
-        }
-        if ($status) {
-            $status = $this->check_dir_exists($CFG->tempdir."/bbquiz_import/".$unique_code,true);
-        }
-
-        return $status;
-    }
-
-    function clean_temp_dir($dir='') {
+require_once($CFG->libdir . '/xmlize.php');
+require_once($CFG->dirroot . '/question/format/blackboard_six/formatbase.php');
+require_once($CFG->dirroot . '/question/format/blackboard_six/formatqti.php');
+require_once($CFG->dirroot . '/question/format/blackboard_six/formatpool.php');
+
+class qformat_blackboard_six extends qformat_blackboard_six_base {
+    /** @var int Blackboard assessment qti files were always imported by the blackboard_six plugin. */
+    const FILETYPE_QTI = 1;
+    /** @var int Blackboard question pool files were previously handled by the blackboard plugin. */
+    const FILETYPE_POOL = 2;
+    /** @var int type of file being imported, one of the constants FILETYPE_QTI or FILETYPE_POOL. */
+    public $filetype;
+
+    public function get_filecontent($path) {
+        $fullpath = $this->tempdir . '/' . $path;
+        if (is_file($fullpath) && is_readable($fullpath)) {
+            return file_get_contents($fullpath);
+        }
+        return false;
+    }
+
+    /**
+     * Set the file type being imported
+     * @param int $type the imported file's type
+     */
+    public function set_filetype($type) {
+        $this->filetype = $type;
+    }
+
+    /**
+     * Return content of all files containing questions,
+     * as an array one element for each file found,
+     * For each file, the corresponding element is an array of lines.
+     * @param string filename name of file
+     * @return mixed contents array or false on failure
+     */
+    public function readdata($filename) {
         global $CFG;
 
-        // for now we will just say everything happened okay note
-        // that a mess may be piling up in $CFG->tempdir/bbquiz_import
-        // TODO return true at top of the function renders all the following code useless
-        return true;
-
-        if ($dir == '') {
-            $dir = $this->temp_dir;
-        }
-        $slash = "/";
-
-        // Create arrays to store files and directories
-        $dir_files      = array();
-        $dir_subdirs    = array();
-
-        // Make sure we can delete it
-        chmod($dir, $CFG->directorypermissions);
-
-        if ((($handle = opendir($dir))) == FALSE) {
-            // The directory could not be opened
-            return false;
-        }
-
-        // Loop through all directory entries, and construct two temporary arrays containing files and sub directories
-        while(false !== ($entry = readdir($handle))) {
-            if (is_dir($dir. $slash .$entry) && $entry != ".." && $entry != ".") {
-                $dir_subdirs[] = $dir. $slash .$entry;
-            }
-            else if ($entry != ".." && $entry != ".") {
-                $dir_files[] = $dir. $slash .$entry;
-            }
-        }
-
-        // Delete all files in the curent directory return false and halt if a file cannot be removed
-        $countdir_files = count($dir_files);
-        for($i=0; $i<$countdir_files; $i++) {
-            chmod($dir_files[$i], $CFG->directorypermissions);
-            if (((unlink($dir_files[$i]))) == FALSE) {
+        // Find if we are importing a .dat file.
+        if (strtolower(pathinfo($filename, PATHINFO_EXTENSION)) == 'dat') {
+            if (!is_readable($filename)) {
+                $this->error(get_string('filenotreadable', 'error'));
                 return false;
             }
-        }
-
-        // Empty sub directories and then remove the directory
-        $countdir_subdirs = count($dir_subdirs);
-        for($i=0; $i<$countdir_subdirs; $i++) {
-            chmod($dir_subdirs[$i], $CFG->directorypermissions);
-            if ($this->clean_temp_dir($dir_subdirs[$i]) == FALSE) {
-                return false;
+            // As we are not importing a .zip file,
+            // there is no imsmanifest, and it is not possible
+            // to parse it to find the file type.
+            // So we need to guess the file type by looking at the content.
+            // For now we will do that searching for a required tag.
+            // This is certainly not bullet-proof but works for all usual files.
+            $text = file_get_contents($filename);
+            if (strpos($text, '<questestinterop>')) {
+                $this->set_filetype(self::FILETYPE_QTI);
             }
-            else {
-                if (rmdir($dir_subdirs[$i]) == FALSE) {
-                return false;
-                }
+            if (strpos($text, '<POOL>')) {
+                $this->set_filetype(self::FILETYPE_POOL);
             }
-        }
+            // In all other cases we are not able to handle this question file.
 
-        // Close directory
-        closedir($handle);
-        if (rmdir($this->temp_dir) == FALSE) {
-            return false;
+            // Readquestions is now expecting an array of strings.
+            return array($text);
         }
-        // Success, every thing is gone return true
-        return true;
-    }
-
-    //Function to check if a directory exists and, optionally, create it
-    function check_dir_exists($dir,$create=false) {
-
-        global $CFG;
-
-        $status = true;
-        if(!is_dir($dir)) {
-            if (!$create) {
-                $status = false;
-            } else {
-                umask(0000);
-                $status = mkdir ($dir,$CFG->directorypermissions);
+        // We are importing a zip file.
+        // Create name for temporary directory.
+        $unique_code = time();
+        $this->tempdir = make_temp_directory('bbquiz_import/' . $unique_code);
+        if (is_readable($filename)) {
+            if (!copy($filename, $this->tempdir . '/bboard.zip')) {
+                $this->error(get_string('cannotcopybackup', 'question'));
+                fulldelete($this->tempdir);
+                return false;
             }
-        }
-        return $status;
-    }
+            if (unzip_file($this->tempdir . '/bboard.zip', '', false)) {
+                $dom = new DomDocument();
 
-    function importpostprocess() {
-    /// Does any post-processing that may be desired
-    /// Argument is a simple array of question ids that
-    /// have just been added.
-
-        // need to clean up temporary directory
-        return $this->clean_temp_dir();
-    }
-
-    function copy_file_to_course($filename) {
-        global $CFG, $COURSE;
-        $filename = str_replace('\\','/',$filename);
-        $fullpath = $this->temp_dir.'/res00001/'.$filename;
-        $basename = basename($filename);
-
-        $copy_to = $CFG->dataroot.'/'.$COURSE->id.'/bb_import';
-
-        if ($this->check_dir_exists($copy_to,true)) {
-            if(is_readable($fullpath)) {
-                $copy_to.= '/'.$basename;
-                if (!copy($fullpath, $copy_to)) {
+                if (!$dom->load($this->tempdir . '/imsmanifest.xml')) {
+                    $this->error(get_string('errormanifest', 'qformat_blackboard_six'));
+                    fulldelete($this->tempdir);
                     return false;
                 }
-                else {
-                    return $copy_to;
-                }
-            }
-        }
-        else {
-            return false;
-        }
-    }
 
-    function readdata($filename) {
-    /// Returns complete file with an array, one item per line
-        global $CFG;
+                $xpath = new DOMXPath($dom);
 
-        // if the extension is .dat we just return that,
-        // if .zip we unzip the file and get the data
-        $ext = substr($this->realfilename, strpos($this->realfilename,'.'), strlen($this->realfilename)-1);
-        if ($ext=='.dat') {
-            if (!is_readable($filename)) {
-                print_error('filenotreadable', 'error');
-            }
-            return file($filename);
-        }
+                // We starts from the root element.
+                $query = '//resources/resource';
+                $this->filebase = $this->tempdir;
+                $q_file = array();
 
-        $unique_code = time();
-        $temp_dir = $CFG->tempdir."/bbquiz_import/".$unique_code;
-        $this->temp_dir = $temp_dir;
-        if ($this->check_and_create_import_dir($unique_code)) {
-            if(is_readable($filename)) {
-                if (!copy($filename, "$temp_dir/bboard.zip")) {
-                    print_error('cannotcopybackup', 'question');
-                }
-                if(unzip_file("$temp_dir/bboard.zip", '', false)) {
-                    // assuming that the information is in res0001.dat
-                    // after looking at 6 examples this was always the case
-                    $q_file = "$temp_dir/res00001.dat";
-                    if (is_file($q_file)) {
-                        if (is_readable($q_file)) {
-                            $filearray = file($q_file);
-                            /// Check for Macintosh OS line returns (ie file on one line), and fix
-                            if (preg_match("~\r~", $filearray[0]) AND !preg_match("~\n~", $filearray[0])) {
-                                return explode("\r", $filearray[0]);
-                            } else {
-                                return $filearray;
-                            }
+                $examfiles = $xpath->query($query);
+                foreach ($examfiles as $examfile) {
+                    if ($examfile->getAttribute('type') == 'assessment/x-bb-qti-test'
+                            || $examfile->getAttribute('type') == 'assessment/x-bb-qti-pool') {
+
+                        if ($content = $this->get_filecontent($examfile->getAttribute('bb:file'))) {
+                            $this->set_filetype(self::FILETYPE_QTI);
+                            $q_file[] = $content;
                         }
                     }
-                    else {
-                        print_error('cannotfindquestionfile', 'questioni');
-                    }
-                }
-                else {
-                    print "filename: $filename<br />tempdir: $temp_dir <br />";
-                    print_error('cannotunzip', 'question');
-                }
-            }
-            else {
-                print_error('cannotreaduploadfile');
-            }
-        }
-        else {
-            print_error('cannotcreatetempdir');
-        }
-    }
-
-    function save_question_options($question) {
-        return true;
-    }
-
-
-
-  protected function readquestions($lines) {
-    /// Parses an array of lines into an array of questions,
-    /// where each item is a question object as defined by
-    /// readquestion().
-
-    $text = implode($lines, " ");
-    $xml = xmlize($text, 0);
-
-    $raw_questions = $xml['questestinterop']['#']['assessment'][0]['#']['section'][0]['#']['item'];
-    $questions = array();
-
-    foreach($raw_questions as $quest) {
-        $question = $this->create_raw_question($quest);
-
-        switch($question->qtype) {
-            case "Matching":
-                $this->process_matching($question, $questions);
-                break;
-            case "Multiple Choice":
-                $this->process_mc($question, $questions);
-                break;
-            case "Essay":
-                $this->process_essay($question, $questions);
-                break;
-            case "Multiple Answer":
-                $this->process_ma($question, $questions);
-                break;
-            case "True/False":
-                $this->process_tf($question, $questions);
-                break;
-            case 'Fill in the Blank':
-                $this->process_fblank($question, $questions);
-                break;
-            case 'Short Response':
-                $this->process_essay($question, $questions);
-                break;
-            default:
-                print "Unknown or unhandled question type: \"$question->qtype\"<br />";
-                break;
-        }
-
-    }
-    return $questions;
-  }
-
-
-// creates a cleaner object to deal with for processing into moodle
-// the object created is NOT a moodle question object
-function create_raw_question($quest) {
-
-    $question = new stdClass();
-    $question->qtype = $quest['#']['itemmetadata'][0]['#']['bbmd_questiontype'][0]['#'];
-    $question->id = $quest['#']['itemmetadata'][0]['#']['bbmd_asi_object_id'][0]['#'];
-    $presentation->blocks = $quest['#']['presentation'][0]['#']['flow'][0]['#']['flow'];
-
-    foreach($presentation->blocks as $pblock) {
-
-        $block = NULL;
-        $block->type = $pblock['@']['class'];
-
-        switch($block->type) {
-            case 'QUESTION_BLOCK':
-                $sub_blocks = $pblock['#']['flow'];
-                foreach($sub_blocks as $sblock) {
-                    //echo "Calling process_block from line 263<br>";
-                    $this->process_block($sblock, $block);
-                }
-                break;
-
-            case 'RESPONSE_BLOCK':
-                $choices = NULL;
-                switch($question->qtype) {
-                    case 'Matching':
-                        $bb_subquestions = $pblock['#']['flow'];
-                        $sub_questions = array();
-                        foreach($bb_subquestions as $bb_subquestion) {
-                            $sub_question = NULL;
-                            $sub_question->ident = $bb_subquestion['#']['response_lid'][0]['@']['ident'];
-                            $this->process_block($bb_subquestion['#']['flow'][0], $sub_question);
-                            $bb_choices = $bb_subquestion['#']['response_lid'][0]['#']['render_choice'][0]['#']['flow_label'][0]['#']['response_label'];
-                            $choices = array();
-                            $this->process_choices($bb_choices, $choices);
-                            $sub_question->choices = $choices;
-                            if (!isset($block->subquestions)) {
-                                $block->subquestions = array();
-                            }
-                            $block->subquestions[] = $sub_question;
+                    if ($examfile->getAttribute('type') == 'assessment/x-bb-pool') {
+                        if ($examfile->getAttribute('baseurl')) {
+                            $this->filebase = $this->tempdir. '/' . $examfile->getAttribute('baseurl');
                         }
-                        break;
-                    case 'Multiple Answer':
-                        $bb_choices = $pblock['#']['response_lid'][0]['#']['render_choice'][0]['#']['flow_label'];
-                        $choices = array();
-                        $this->process_choices($bb_choices, $choices);
-                        $block->choices = $choices;
-                        break;
-                    case 'Essay':
-                        // Doesn't apply since the user responds with text input
-                        break;
-                    case 'Multiple Choice':
-                        $mc_choices = $pblock['#']['response_lid'][0]['#']['render_choice'][0]['#']['flow_label'];
-                            foreach($mc_choices as $mc_choice) {
-                            $choices = NULL;
-                            $choices = $this->process_block($mc_choice, $choices);
-                            $block->choices[] = $choices;
+                        if ($content = $this->get_filecontent($examfile->getAttribute('file'))) {
+                            $this->set_filetype(self::FILETYPE_POOL);
+                            $q_file[] = $content;
                         }
-                        break;
-                    case 'Short Response':
-                        // do nothing?
-                        break;
-                    case 'Fill in the Blank':
-                        // do nothing?
-                        break;
-                    default:
-                        $bb_choices = $pblock['#']['response_lid'][0]['#']['render_choice'][0]['#']['flow_label'][0]['#']['response_label'];
-                        $choices = array();
-                        $this->process_choices($bb_choices, $choices);
-                        $block->choices = $choices;
-                }
-                break;
-            case 'RIGHT_MATCH_BLOCK':
-                $matching_answerset = $pblock['#']['flow'];
-
-                $answerset = array();
-                foreach($matching_answerset as $answer) {
-                    // $answerset[] = $this->process_block($answer, $bb_answer);
-                    $bb_answer = null;
-                    $bb_answer->text = $answer['#']['flow'][0]['#']['material'][0]['#']['mat_extension'][0]['#']['mat_formattedtext'][0]['#'];
-                    $answerset[] = $bb_answer;
-                }
-                $block->matching_answerset = $answerset;
-                break;
-            default:
-                print "UNHANDLED PRESENTATION BLOCK";
-                break;
-        }
-        $question->{$block->type} = $block;
-    }
-
-    // determine response processing
-    // there is a section called 'outcomes' that I don't know what to do with
-    $resprocessing = $quest['#']['resprocessing'];
-    $respconditions = $resprocessing[0]['#']['respcondition'];
-    $reponses = array();
-    if ($question->qtype == 'Matching') {
-        $this->process_matching_responses($respconditions, $responses);
-    }
-    else {
-        $this->process_responses($respconditions, $responses);
-    }
-    $question->responses = $responses;
-    $feedbackset = $quest['#']['itemfeedback'];
-    $feedbacks = array();
-    $this->process_feedback($feedbackset, $feedbacks);
-    $question->feedback = $feedbacks;
-    return $question;
-}
-
-function process_block($cur_block, &$block) {
-    global $COURSE, $CFG;
-
-    $cur_type = $cur_block['@']['class'];
-    switch($cur_type) {
-        case 'FORMATTED_TEXT_BLOCK':
-            $block->text = $this->strip_applet_tags_get_mathml($cur_block['#']['material'][0]['#']['mat_extension'][0]['#']['mat_formattedtext'][0]['#']);
-            break;
-        case 'FILE_BLOCK':
-            //revisit this to make sure it is working correctly
-            // Commented out ['matapplication']..., etc. because I
-            // noticed that when I imported a new Blackboard 6 file
-            // and printed out the block, the tree did not extend past ['material'][0]['#'] - CT 8/3/06
-            $block->file = $cur_block['#']['material'][0]['#'];//['matapplication'][0]['@']['uri'];
-            if ($block->file != '') {
-                // if we have a file copy it to the course dir and adjust its name to be visible over the web.
-                $block->file = $this->copy_file_to_course($block->file);
-                $block->file = $CFG->wwwroot.'/file.php/'.$COURSE->id.'/bb_import/'.basename($block->file);
-            }
-            break;
-        case 'Block':
-            if (isset($cur_block['#']['material'][0]['#']['mattext'][0]['#'])) {
-            $block->text = $cur_block['#']['material'][0]['#']['mattext'][0]['#'];
-            }
-            else if (isset($cur_block['#']['material'][0]['#']['mat_extension'][0]['#']['mat_formattedtext'][0]['#'])) {
-                $block->text = $cur_block['#']['material'][0]['#']['mat_extension'][0]['#']['mat_formattedtext'][0]['#'];
-            }
-            else if (isset($cur_block['#']['response_label'])) {
-                // this is a response label block
-                $sub_blocks = $cur_block['#']['response_label'][0];
-                if(!isset($block->ident)) {
-                    if(isset($sub_blocks['@']['ident'])) {
-                        $block->ident = $sub_blocks['@']['ident'];
                     }
                 }
-                foreach($sub_blocks['#']['flow_mat'] as $sub_block) {
-                    $this->process_block($sub_block, $block);
-                }
-            }
-            else {
-                if (isset($cur_block['#']['flow_mat']) || isset($cur_block['#']['flow'])) {
-                    if (isset($cur_block['#']['flow_mat'])) {
-                        $sub_blocks = $cur_block['#']['flow_mat'];
-                    }
-                    elseif (isset($cur_block['#']['flow'])) {
-                        $sub_blocks = $cur_block['#']['flow'];
-                    }
-                   foreach ($sub_blocks as $sblock) {
-                        // this will recursively grab the sub blocks which should be of one of the other types
-                        $this->process_block($sblock, $block);
-                    }
-                }
-            }
-            break;
-        case 'LINK_BLOCK':
-            // not sure how this should be included
-            if (!empty($cur_block['#']['material'][0]['#']['mattext'][0]['@']['uri'])) {
-                $block->link = $cur_block['#']['material'][0]['#']['mattext'][0]['@']['uri'];
-            }
-            else {
-               $block->link = '';
-            }
-            break;
-    }
-    return $block;
-}
 
-function process_choices($bb_choices, &$choices) {
-    foreach($bb_choices as $choice) {
-            if (isset($choice['@']['ident'])) {
-            $cur_choice = $choice['@']['ident'];
-        }
-        else { //for multiple answer
-            $cur_choice = $choice['#']['response_label'][0];//['@']['ident'];
-        }
-        if (isset($choice['#']['flow_mat'][0])) { //for multiple answer
-            $cur_block = $choice['#']['flow_mat'][0];
-            // Reset $cur_choice to NULL because process_block is expecting an object
-            // for the second argument and not a string, which is what is was set as
-            // originally - CT 8/7/06
-            $cur_choice = null;
-            $this->process_block($cur_block, $cur_choice);
-        }
-        elseif (isset($choice['#']['response_label'])) {
-            // Reset $cur_choice to NULL because process_block is expecting an object
-            // for the second argument and not a string, which is what is was set as
-            // originally - CT 8/7/06
-            $cur_choice = null;
-            $this->process_block($choice, $cur_choice);
-        }
-        $choices[] = $cur_choice;
-    }
-}
-
-function process_matching_responses($bb_responses, &$responses) {
-    foreach($bb_responses as $bb_response) {
-        $response = NULL;
-        if (isset($bb_response['#']['conditionvar'][0]['#']['varequal'])) {
-            $response->correct = $bb_response['#']['conditionvar'][0]['#']['varequal'][0]['#'];
-            $response->ident = $bb_response['#']['conditionvar'][0]['#']['varequal'][0]['@']['respident'];
-        }
-        else {
-            $response->correct =  'Broken Question?';
-            $response->ident = 'Broken Question?';
-        }
-        $response->feedback = $bb_response['#']['displayfeedback'][0]['@']['linkrefid'];
-        $responses[] = $response;
-    }
-}
-
-function process_responses($bb_responses, &$responses) {
-    foreach($bb_responses as $bb_response) {
-        //Added this line to instantiate $response.
-        // Without instantiating the $response variable, the same object
-        // gets added to the array
-        $response = new stdClass();
-        if (isset($bb_response['@']['title'])) {
-                $response->title = $bb_response['@']['title'];
-            }
-            else {
-                $reponse->title = $bb_response['#']['displayfeedback'][0]['@']['linkrefid'];
-            }
-            $reponse->ident = array();
-            if (isset($bb_response['#']['conditionvar'][0]['#'])){//['varequal'][0]['#'])) {
-                $response->ident[0] = $bb_response['#']['conditionvar'][0]['#'];//['varequal'][0]['#'];
-            }
-            else if (isset($bb_response['#']['conditionvar'][0]['#']['other'][0]['#'])) {
-                $response->ident[0] = $bb_response['#']['conditionvar'][0]['#']['other'][0]['#'];
-            }
-
-            if (isset($bb_response['#']['conditionvar'][0]['#']['and'])){//[0]['#'])) {
-                $responseset = $bb_response['#']['conditionvar'][0]['#']['and'];//[0]['#']['varequal'];
-                foreach($responseset as $rs) {
-                    $response->ident[] = $rs['#'];
-                    if(!isset($response->feedback) and isset( $rs['@'] ) ) {
-                        $response->feedback = $rs['@']['respident'];
-                    }
-                }
-            }
-            else {
-                $response->feedback = $bb_response['#']['displayfeedback'][0]['@']['linkrefid'];
-            }
-
-            // determine what point value to give response
-            if (isset($bb_response['#']['setvar'])) {
-                switch ($bb_response['#']['setvar'][0]['#']) {
-                    case "SCORE.max":
-                        $response->fraction = 1;
-                        break;
-                    default:
-                        // I have only seen this being 0 or unset
-                        // there are probably fractional values of SCORE.max, but I'm not sure what they look like
-                        $response->fraction = 0;
-                        break;
-                }
-            }
-            else {
-               // just going to assume this is the case this is probably not correct.
-               $response->fraction = 0;
-            }
-
-            $responses[] = $response;
-        }
-}
-
-function process_feedback($feedbackset, &$feedbacks) {
-    foreach($feedbackset as $bb_feedback) {
-        // Added line $feedback=null so that $feedback does not get reused in the loop
-        // and added the the $feedbacks[] array multiple times
-        $feedback = null;
-        $feedback->ident = $bb_feedback['@']['ident'];
-        if (isset($bb_feedback['#']['flow_mat'][0])) {
-            $this->process_block($bb_feedback['#']['flow_mat'][0], $feedback);
-        }
-        elseif (isset($bb_feedback['#']['solution'][0]['#']['solutionmaterial'][0]['#']['flow_mat'][0])) {
-            $this->process_block($bb_feedback['#']['solution'][0]['#']['solutionmaterial'][0]['#']['flow_mat'][0], $feedback);
-        }
-        $feedbacks[] = $feedback;
-    }
-}
-
-/**
- * Create common parts of question
- */
-function process_common( $quest ) {
-    $question = $this->defaultquestion();
-    $question->questiontext = $quest->QUESTION_BLOCK->text;
-    $question->name = shorten_text( $quest->id, 250 );
-
-    return $question;
-}
-
-//----------------------------------------
-// Process True / False Questions
-//----------------------------------------
-function process_tf($quest, &$questions) {
-    $question = $this->process_common( $quest );
-
-    $question->qtype = TRUEFALSE;
-    $question->single = 1; // Only one answer is allowed
-    // 0th [response] is the correct answer.
-    $responses = $quest->responses;
-    $correctresponse = $responses[0]->ident[0]['varequal'][0]['#'];
-    if ($correctresponse != 'false') {
-        $correct = true;
-    }
-    else {
-        $correct = false;
-    }
-
-    foreach($quest->feedback as $fb) {
-        $fback->{$fb->ident} = $fb->text;
-    }
-
-    if ($correct) {  // true is correct
-        $question->answer = 1;
-        $question->feedbacktrue = $fback->correct;
-        $question->feedbackfalse = $fback->incorrect;
-    } else {  // false is correct
-        $question->answer = 0;
-        $question->feedbacktrue = $fback->incorrect;
-        $question->feedbackfalse = $fback->correct;
-    }
-    $question->correctanswer = $question->answer;
-    $questions[] = $question;
-}
-
-
-//----------------------------------------
-// Process Fill in the Blank
-//----------------------------------------
-function process_fblank($quest, &$questions) {
-    $question = $this->process_common( $quest );
-    $question->qtype = SHORTANSWER;
-    $question->single = 1;
-
-    $answers = array();
-    $fractions = array();
-    $feedbacks = array();
-
-    // extract the feedback
-    $feedback = array();
-    foreach($quest->feedback as $fback) {
-        if (isset($fback->ident)) {
-            if ($fback->ident == 'correct' || $fback->ident == 'incorrect') {
-                $feedback[$fback->ident] = $fback->text;
-            }
-        }
-    }
-
-    foreach($quest->responses as $response) {
-        if(isset($response->title)) {
-            if (isset($response->ident[0]['varequal'][0]['#'])) {
-                //for BB Fill in the Blank, only interested in correct answers
-                if ($response->feedback = 'correct') {
-                    $answers[] = $response->ident[0]['varequal'][0]['#'];
-                    $fractions[] = 1;
-                    if (isset($feedback['correct'])) {
-                        $feedbacks[] = $feedback['correct'];
-                    }
-                    else {
-                        $feedbacks[] = '';
-                    }
+                if ($q_file) {
+                    return $q_file;
+                } else {
+                    $this->error(get_string('cannotfindquestionfile', 'question'));
+                    fulldelete($this->tempdir);
                 }
-            }
-
-        }
-    }
-
-    //Adding catchall to so that students can see feedback for incorrect answers when they enter something the
-    //instructor did not enter
-    $answers[] = '*';
-    $fractions[] = 0;
-    if (isset($feedback['incorrect'])) {
-        $feedbacks[] = $feedback['incorrect'];
-    }
-    else {
-        $feedbacks[] = '';
-    }
-
-    $question->answer = $answers;
-    $question->fraction = $fractions;
-    $question->feedback = $feedbacks; // Changed to assign $feedbacks to $question->feedback instead of
-
-    if (!empty($question)) {
-        $questions[] = $question;
-    }
-
-}
-
-//----------------------------------------
-// Process Multiple Choice Questions
-//----------------------------------------
-function process_mc($quest, &$questions) {
-    $question = $this->process_common( $quest );
-    $question->qtype = MULTICHOICE;
-    $question->single = 1;
-
-    $feedback = array();
-    foreach($quest->feedback as $fback) {
-        $feedback[$fback->ident] = $fback->text;
-    }
-
-    foreach($quest->responses as $response) {
-        if (isset($response->title)) {
-            if ($response->title == 'correct') {
-                // only one answer possible for this qtype so first index is correct answer
-                $correct = $response->ident[0]['varequal'][0]['#'];
-            }
-        }
-        else {
-            // fallback method for when the title is not set
-            if ($response->feedback == 'correct') {
-               // only one answer possible for this qtype so first index is correct answer
-               $correct = $response->ident[0]['varequal'][0]['#']; // added [0]['varequal'][0]['#'] to $response->ident - CT 8/9/06
-            }
-        }
-    }
-
-    $i = 0;
-    foreach($quest->RESPONSE_BLOCK->choices as $response) {
-        $question->answer[$i] = $response->text;
-        if ($correct == $response->ident) {
-            $question->fraction[$i] = 1;
-            // this is a bit of a hack to catch the feedback... first we see if a 'correct' feedback exists
-            // then specific feedback for this question (maybe this should be switched?, but from my example
-            // question pools I have not seen response specific feedback, only correct or incorrect feedback
-            if (!empty($feedback['correct'])) {
-                $question->feedback[$i] = $feedback['correct'];
-            }
-            elseif (!empty($feedback[$i])) {
-                $question->feedback[$i] = $feedback[$i];
-            }
-            else {
-                // failsafe feedback (should be '' instead?)
-                $question->feedback[$i] = "correct";
-            }
-        }
-        else {
-            $question->fraction[$i] = 0;
-            if (!empty($feedback['incorrect'])) {
-                $question->feedback[$i] = $feedback['incorrect'];
-            }
-            elseif (!empty($feedback[$i])) {
-                $question->feedback[$i] = $feedback[$i];
-            }
-            else {
-                // failsafe feedback (should be '' instead?)
-                $question->feedback[$i] = 'incorrect';
-            }
-        }
-        $i++;
-    }
-
-    if (!empty($question)) {
-        $questions[] = $question;
-    }
-}
-
-//----------------------------------------
-// Process Multiple Choice Questions With Multiple Answers
-//----------------------------------------
-function process_ma($quest, &$questions) {
-    $question = $this->process_common( $quest ); // copied this from process_mc
-    $question->qtype = MULTICHOICE;
-    $question->single = 0; // More than one answer allowed
-
-    $answers = $quest->responses;
-    $correct_answers = array();
-    foreach($answers as $answer) {
-        if($answer->title == 'correct') {
-            $answerset = $answer->ident[0]['and'][0]['#']['varequal'];
-            foreach($answerset as $ans) {
-                $correct_answers[] = $ans['#'];
-            }
-        }
-    }
-
-    foreach ($quest->feedback as $fb) {
-        $feedback->{$fb->ident} = trim($fb->text);
-    }
-
-    $correct_answer_count = count($correct_answers);
-    $choiceset = $quest->RESPONSE_BLOCK->choices;
-    $i = 0;
-    foreach($choiceset as $choice) {
-        $question->answer[$i] = trim($choice->text);
-        if (in_array($choice->ident, $correct_answers)) {
-            // correct answer
-            $question->fraction[$i] = floor(100000/$correct_answer_count)/100000; // strange behavior if we have more than 5 decimal places
-            $question->feedback[$i] = $feedback->correct;
-        }
-        else {
-            // wrong answer
-            $question->fraction[$i] = 0;
-            $question->feedback[$i] = $feedback->incorrect;
-        }
-        $i++;
-    }
-
-    $questions[] = $question;
-}
-
-//----------------------------------------
-// Process Essay Questions
-//----------------------------------------
-function process_essay($quest, &$questions) {
-// this should be rewritten to accomodate moodle 1.6 essay question type eventually
-
-    if (defined("ESSAY")) {
-        // treat as short answer
-        $question = $this->process_common( $quest ); // copied this from process_mc
-        $question->qtype = ESSAY;
-
-        $question->feedback = array();
-        // not sure where to get the correct answer from
-        foreach($quest->feedback as $feedback) {
-            // Added this code to put the possible solution that the
-            // instructor gives as the Moodle answer for an essay question
-            if ($feedback->ident == 'solution') {
-                $question->feedback = $feedback->text;
-            }
-        }
-        //Added because essay/questiontype.php:save_question_option is expecting a
-        //fraction property - CT 8/10/06
-        $question->fraction[] = 1;
-        if (!empty($question)) {
-            $questions[]=$question;
-        }
-    }
-    else {
-        print "Essay question types are not handled because the quiz question type 'Essay' does not exist in this installation of Moodle<br/>";
-        print "&nbsp;&nbsp;&nbsp;&nbsp;Omitted Question: ".$quest->QUESTION_BLOCK->text.'<br/><br/>';
-    }
-}
-
-//----------------------------------------
-// Process Matching Questions
-//----------------------------------------
-function process_matching($quest, &$questions) {
-    // renderedmatch is an optional plugin, so we need to check if it is defined
-    if (question_bank::is_qtype_installed('renderedmatch')) {
-        $question = $this->process_common($quest);
-        $question->valid = true;
-        $question->qtype = 'renderedmatch';
-
-        foreach($quest->RESPONSE_BLOCK->subquestions as $qid => $subq) {
-            foreach($quest->responses as $rid => $resp) {
-                if ($resp->ident == $subq->ident) {
-                    $correct = $resp->correct;
-                    $feedback = $resp->feedback;
-                }
-            }
-
-            foreach($subq->choices as $cid => $choice) {
-                if ($choice == $correct) {
-                    $question->subquestions[] = $subq->text;
-                    $question->subanswers[] = $quest->RIGHT_MATCH_BLOCK->matching_answerset[$cid]->text;
-                }
-            }
-        }
-
-        // check format
-        $status = true;
-        if ( count($quest->RESPONSE_BLOCK->subquestions) > count($quest->RIGHT_MATCH_BLOCK->matching_answerset) || count($question->subquestions) < 2) {
-            $status = false;
-        }
-        else {
-            // need to redo to make sure that no two questions have the same answer (rudimentary now)
-            foreach($question->subanswers as $qstn) {
-                if(isset($previous)) {
-                    if ($qstn == $previous) {
-                        $status = false;
-                    }
-                }
-                $previous = $qstn;
-                if ($qstn == '') {
-                    $status = false;
-                }
-            }
-        }
-
-        if ($status) {
-            $questions[] = $question;
-        }
-        else {
-            global $COURSE, $CFG;
-            print '<table class="boxaligncenter" border="1">';
-            print '<tr><td colspan="2" style="background-color:#FF8888;">This matching question is malformed. Please ensure there are no blank answers, no two questions have the same answer, and/or there are correct answers for each question. There must be at least as many subanswers as subquestions, and at least one subquestion.</td></tr>';
-
-            print "<tr><td>Question:</td><td>".$quest->QUESTION_BLOCK->text;
-            if (isset($quest->QUESTION_BLOCK->file)) {
-                print '<br/><font color="red">There is a subfile contained in the zipfile that has been copied to course files: bb_import/'.basename($quest->QUESTION_BLOCK->file).'</font>';
-                if (preg_match('/(gif|jpg|jpeg|png)$/i', $quest->QUESTION_BLOCK->file)) {
-                    print '<img src="'.$CFG->wwwroot.'/file.php/'.$COURSE->id.'/bb_import/'.basename($quest->QUESTION_BLOCK->file).'" />';
-                }
-            }
-            print "</td></tr>";
-            print "<tr><td>Subquestions:</td><td><ul>";
-            foreach($quest->responses as $rs) {
-                $correct_responses->{$rs->ident} = $rs->correct;
-            }
-            foreach($quest->RESPONSE_BLOCK->subquestions as $subq) {
-                print '<li>'.$subq->text.'<ul>';
-                foreach($subq->choices as $id=>$choice) {
-                    print '<li>';
-                    if ($choice == $correct_responses->{$subq->ident}) {
-                        print '<font color="green">';
-                    }
-                    else {
-                        print '<font color="red">';
-                    }
-                    print $quest->RIGHT_MATCH_BLOCK->matching_answerset[$id]->text.'</font></li>';
-                }
-                print '</ul>';
-            }
-            print '</ul></td></tr>';
-
-            print '<tr><td>Feedback:</td><td><ul>';
-            foreach($quest->feedback as $fb) {
-                print '<li>'.$fb->ident.': '.$fb->text.'</li>';
-            }
-            print '</ul></td></tr></table>';
+            } else {
+                $this->error(get_string('cannotunzip', 'question'));
+                fulldelete($this->temp_dir);
+            }
+        } else {
+            $this->error(get_string('cannotreaduploadfile', 'error'));
+            fulldelete($this->tempdir);
+        }
+        return false;
+    }
+
+    /**
+     * Parse the array of strings into an array of questions.
+     * Each string is the content of a .dat questions file.
+     * This *could* burn memory - but it won't happen that much
+     * so fingers crossed!
+     * @param array of strings from the input file.
+     * @param stdClass $context
+     * @return array (of objects) question objects.
+     */
+    public function readquestions($lines) {
+
+        // Set up array to hold all our questions.
+        $questions = array();
+        if ($this->filetype == self::FILETYPE_QTI) {
+            $importer = new qformat_blackboard_six_qti();
+        } else if ($this->filetype == self::FILETYPE_POOL) {
+            $importer = new qformat_blackboard_six_pool();
+        } else {
+            // In all other cases we are not able to import the file.
+            return false;
         }
-    }
-    else {
-        print "Matching question types are not handled because the quiz question type 'Rendered Matching' does not exist in this installation of Moodle<br/>";
-        print "&nbsp;&nbsp;&nbsp;&nbsp;Omitted Question: ".$quest->QUESTION_BLOCK->text.'<br/><br/>';
-    }
-}
+        $importer->set_filebase($this->filebase);
 
-
-function strip_applet_tags_get_mathml($string) {
-    if(stristr($string, '</APPLET>') === FALSE) {
-        return $string;
-    }
-    else {
-        // strip all applet tags keeping stuff before/after and inbetween (if mathml) them
-        while (stristr($string, '</APPLET>') !== FALSE) {
-            preg_match("/(.*)\<applet.*value=\"(\<math\>.*\<\/math\>)\".*\<\/applet\>(.*)/i",$string, $mathmls);
-            $string = $mathmls[1].$mathmls[2].$mathmls[3];
+        // Each element of $lines is a string containing a complete xml document.
+        foreach ($lines as $text) {
+                $questions = array_merge($questions, $importer->readquestions($text));
         }
-        return $string;
+        return $questions;
     }
 }
-
-} // close object
-
diff --git a/question/format/blackboard_six/formatbase.php b/question/format/blackboard_six/formatbase.php
new file mode 100644 (file)
index 0000000..da08e91
--- /dev/null
@@ -0,0 +1,163 @@
+<?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/>.
+
+/**
+ * Blackboard V5 and V6 question importer.
+ *
+ * @package    qformat_blackboard_six
+ * @copyright  2012 Jean-Michel Vedrine
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Base class question import format for zip files with images
+ *
+ */
+
+class qformat_blackboard_six_base extends qformat_based_on_xml {
+    /** @var string path to path to root of image tree in unzipped archive. */
+    public $filebase = '';
+    /** @var string path to the temporary directory. */
+    public $tempdir = '';
+    /**
+     * This plugin provide import
+     * @return bool true
+     */
+    public function provide_import() {
+        return true;
+    }
+
+    /**
+     * Check if the given file is capable of being imported by this plugin.
+     * As {@link file_storage::mimetype()} now uses finfo PHP extension if available,
+     * the value returned by $file->get_mimetype for a .dat file is not the same on all servers.
+     * So we must made 2 checks to verify if the plugin can import the file.
+     * @param stored_file $file the file to check
+     * @return bool whether this plugin can import the file
+     */
+    public function can_import_file($file) {
+        $mimetypes = array(
+            mimeinfo('type', '.dat'),
+            mimeinfo('type', '.zip')
+        );
+        return in_array($file->get_mimetype(), $mimetypes) || in_array(mimeinfo('type', $file->get_filename()), $mimetypes);
+    }
+
+    public function mime_type() {
+        return mimeinfo('type', '.zip');
+    }
+
+    /**
+     * Does any post-processing that may be desired
+     * Clean the temporary directory if a zip file was imported
+     * @return bool success
+     */
+    public function importpostprocess() {
+        if ($this->tempdir != '') {
+            fulldelete($this->tempdir);
+        }
+        return true;
+    }
+    /**
+     * Set the path to the root of images tree
+     * @param string $path path to images root
+     */
+    public function set_filebase($path) {
+        $this->filebase = $path;
+    }
+
+    /**
+     * Store an image file in a draft filearea
+     * @param array $text, if itemid element don't exists it will be created
+     * @param string tempdir path to root of image tree
+     * @param string filepathinsidetempdir path to image in the tree
+     * @param string filename image's name
+     * @return string new name of the image as it was stored
+     */
+    protected function store_file_for_text_field(&$text, $tempdir, $filepathinsidetempdir, $filename) {
+        global $USER;
+        $fs = get_file_storage();
+        if (empty($text['itemid'])) {
+            $text['itemid'] = file_get_unused_draft_itemid();
+        }
+        // As question file areas don't support subdirs,
+        // convert path to filename.
+        // So that images with same name can be imported.
+        $newfilename = clean_param(str_replace('/', '__', $filepathinsidetempdir . '__' . $filename), PARAM_FILE);
+        $filerecord = array(
+            'contextid' => context_user::instance($USER->id)->id,
+            'component' => 'user',
+            'filearea'  => 'draft',
+            'itemid'    => $text['itemid'],
+            'filepath'  => '/',
+            'filename'  => $newfilename,
+        );
+        $fs->create_file_from_pathname($filerecord, $tempdir . '/' . $filepathinsidetempdir . '/' . $filename);
+        return $newfilename;
+    }
+
+    /**
+     * Given an HTML text with references to images files,
+     * store all images in a draft filearea,
+     * and return an array with all urls in text recoded,
+     * format set to FORMAT_HTML, and itemid set to filearea itemid
+     * @param string text text to parse and recode
+     * @return array with keys text, format, itemid.
+     */
+    public function text_field($text) {
+        $data = array();
+        // Step one, find all file refs then add to array.
+        preg_match_all('|<img[^>]+src="([^"]*)"|i', $text, $out); // Find all src refs.
+
+        foreach ($out[1] as $path) {
+            $fullpath = $this->filebase . '/' . $path;
+
+            if (is_readable($fullpath)) {
+                $dirpath = dirname($path);
+                $filename = basename($path);
+                $newfilename = $this->store_file_for_text_field($data, $this->filebase, $dirpath, $filename);
+                $text = preg_replace("|$path|", "@@PLUGINFILE@@/" . $newfilename, $text);
+            }
+
+        }
+        $data['text'] = $text;
+        $data['format'] = FORMAT_HTML;
+        return $data;
+    }
+
+    /**
+     * Same as text_field but text is cleaned.
+     * @param string text text to parse and recode
+     * @return array with keys text, format, itemid.
+     */
+    public function cleaned_text_field($text) {
+        return $this->text_field($this->cleaninput($text));
+    }
+
+    /**
+     * Convert the question text to plain text.
+     * We need to overwrite this function because questiontext is an array.
+     */
+    protected function format_question_text($question) {
+        global $DB;
+        $formatoptions = new stdClass();
+        $formatoptions->noclean = true;
+        return html_to_text(format_text($question->questiontext['text'],
+                $question->questiontext['format'], $formatoptions), 0, false);
+    }
+}
diff --git a/question/format/blackboard_six/formatpool.php b/question/format/blackboard_six/formatpool.php