Merge branch 'wip-MDL-21097-m23' of git://github.com/marinaglancy/moodle into MOODLE_...
authorEloy Lafuente (stronk7) <stronk7@moodle.org>
Tue, 4 Jun 2013 23:43:34 +0000 (01:43 +0200)
committerEloy Lafuente (stronk7) <stronk7@moodle.org>
Tue, 4 Jun 2013 23:43:34 +0000 (01:43 +0200)
36 files changed:
admin/cli/fix_deleted_users.php [new file with mode: 0644]
auth/ldap/auth.php
course/edit_form.php
course/externallib.php
enrol/imsenterprise/lib.php
enrol/paypal/edit_form.php
grade/import/csv/index.php
install/lang/lb/admin.php [new file with mode: 0644]
install/lang/lb/langconfig.php [new file with mode: 0644]
install/lang/uk/admin.php
lib/completionlib.php
lib/filelib.php
lib/moodlelib.php
lib/navigationlib.php
lib/tests/completionlib_advanced_test.php [new file with mode: 0644]
lib/tests/completionlib_test.php
lib/tests/environment_test.php [new file with mode: 0644]
lib/tests/moodlelib_test.php
lib/tests/navigationlib_test.php
lib/xsendfilelib.php
mod/assign/gradingtable.php
mod/assign/locallib.php
mod/feedback/mapcourse.php
mod/scorm/report/basic/lang/en/scormreport_basic.php
question/engine/lib.php
question/type/multichoice/question.php
question/type/multichoice/tests/question_multi_test.php [new file with mode: 0644]
question/type/multichoice/tests/question_single_test.php [moved from question/type/multichoice/tests/question_test.php with 56% similarity]
report/progress/lib.php
repository/manage_instances.php
theme/sky_high/pix/footer-rtl.png [new file with mode: 0644]
theme/sky_high/pix/footer.png
theme/sky_high/style/admin.css
theme/sky_high/style/pagelayout.css
theme/sky_high/style/report.css
version.php

diff --git a/admin/cli/fix_deleted_users.php b/admin/cli/fix_deleted_users.php
new file mode 100644 (file)
index 0000000..d2a8950
--- /dev/null
@@ -0,0 +1,91 @@
+<?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 script fixed incorrectly deleted users.
+ *
+ * @package    core
+ * @subpackage cli
+ * @copyright  2013 Petr Skoda (http://skodak.org)
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+define('CLI_SCRIPT', true);
+
+require(__DIR__.'/../../config.php');
+require_once($CFG->libdir.'/clilib.php');
+
+
+// Now get cli options.
+list($options, $unrecognized) = cli_get_params(array('help'=>false),
+    array('h'=>'help'));
+
+if ($unrecognized) {
+    $unrecognized = implode("\n  ", $unrecognized);
+    cli_error(get_string('cliunknowoption', 'admin', $unrecognized));
+}
+
+if ($options['help']) {
+    $help =
+        "Fix incorrectly deleted users.
+
+        This scripts detects users that are marked as deleted instead
+        of calling delete_user().
+
+        Deleted users do not have original username, idnumber or email,
+        we must also delete all roles, enrolments, group memberships, etc.
+
+        Please note this script does not delete any public information
+        such as forum posts.
+
+        Options:
+        -h, --help            Print out this help
+
+        Example:
+        \$sudo -u www-data /usr/bin/php admin/cli/fix_deleted_users.php
+        ";
+
+    echo $help;
+    die;
+}
+
+cli_heading('Looking for sloppy user deletes');
+
+// Look for sloppy deleted users where somebody only flipped the deleted flag.
+$sql = "SELECT *
+          FROM {user}
+         WHERE deleted = 1 AND email LIKE '%@%' AND username NOT LIKE '%@%'";
+$rs = $DB->get_recordset_sql($sql);
+foreach ($rs as $user) {
+    echo "Redeleting user $user->id: $user->username ($user->email)\n";
+    delete_user($user);
+}
+
+cli_heading('Deleting all leftovers');
+
+$DB->set_field('user', 'idnumber', '', array('deleted'=>1));
+
+$DB->delete_records_select('role_assignments', "userid IN (SELECT id FROM {user} WHERE deleted = 1)");
+$DB->delete_records_select('cohort_members', "userid IN (SELECT id FROM {user} WHERE deleted = 1)");
+$DB->delete_records_select('groups_members', "userid IN (SELECT id FROM {user} WHERE deleted = 1)");
+$DB->delete_records_select('user_enrolments', "userid IN (SELECT id FROM {user} WHERE deleted = 1)");
+$DB->delete_records_select('user_preferences', "userid IN (SELECT id FROM {user} WHERE deleted = 1)");
+$DB->delete_records_select('user_info_data', "userid IN (SELECT id FROM {user} WHERE deleted = 1)");
+$DB->delete_records_select('user_lastaccess', "userid IN (SELECT id FROM {user} WHERE deleted = 1)");
+$DB->delete_records_select('external_tokens', "userid IN (SELECT id FROM {user} WHERE deleted = 1)");
+$DB->delete_records_select('external_services_users', "userid IN (SELECT id FROM {user} WHERE deleted = 1)");
+
+exit(0);
index 1a6bac8..fa7c2c5 100644 (file)
@@ -549,6 +549,8 @@ class auth_plugin_ldap extends auth_plugin_base {
                 if ($user->firstaccess == 0) {
                     $DB->set_field('user', 'firstaccess', time(), array('id'=>$user->id));
                 }
+                $euser = $DB->get_record('user', array('id' => $user->id));
+                events_trigger('user_updated', $euser);
                 return AUTH_CONFIRM_OK;
             }
         } else {
@@ -710,6 +712,8 @@ class auth_plugin_ldap extends auth_plugin_base {
                         $updateuser->auth = 'nologin';
                         $DB->update_record('user', $updateuser);
                         echo "\t"; print_string('auth_dbsuspenduser', 'auth_db', array('name'=>$user->username, 'id'=>$user->id)); echo "\n";
+                        $euser = $DB->get_record('user', array('id' => $user->id));
+                        events_trigger('user_updated', $euser);
                     }
                 }
             } else {
@@ -735,6 +739,8 @@ class auth_plugin_ldap extends auth_plugin_base {
                     $updateuser->auth = $this->authtype;
                     $DB->update_record('user', $updateuser);
                     echo "\t"; print_string('auth_dbreviveduser', 'auth_db', array('name'=>$user->username, 'id'=>$user->id)); echo "\n";
+                    $euser = $DB->get_record('user', array('id' => $user->id));
+                    events_trigger('user_updated', $euser);
                 }
             } else {
                 print_string('nouserentriestorevive', 'auth_ldap');
@@ -848,6 +854,8 @@ class auth_plugin_ldap extends auth_plugin_base {
 
                 $id = $DB->insert_record('user', $user);
                 echo "\t"; print_string('auth_dbinsertuser', 'auth_db', array('name'=>$user->username, 'id'=>$id)); echo "\n";
+                $euser = $DB->get_record('user', array('id' => $user->id));
+                events_trigger('user_created', $euser);
                 if (!empty($this->config->forcechangepassword)) {
                     set_user_preference('auth_forcepasswordchange', 1, $id);
                 }
@@ -918,6 +926,10 @@ class auth_plugin_ldap extends auth_plugin_base {
                     }
                 }
             }
+            if (!empty($updatekeys)) {
+                $euser = $DB->get_record('user', array('id' => $userid));
+                events_trigger('user_updated', $euser);
+            }
         } else {
             return false;
         }
index 7d22c94..a41d0b1 100644 (file)
@@ -130,6 +130,15 @@ class course_edit_form extends moodleform {
         if (!isset($max) || !is_numeric($max)) {
             $max = 52;
         }
+
+        // Increase the number of sections combo box values if the user has increased the number of sections
+        // using the icon on the course page beyond course 'maxsections' or course 'maxsections' has been
+        // reduced below the number of sections already set for the course on the site administration course
+        // defaults page.  This is so that the number of sections is not reduced leaving unintended orphaned
+        // activities / resources.
+        if (isset($course->numsections) && $course->numsections > $max) {
+            $max = $course->numsections;
+        }
         for ($i = 0; $i <= $max; $i++) {
             $sectionmenu[$i] = "$i";
         }
index 0eeb13e..1a79201 100644 (file)
@@ -678,9 +678,9 @@ class core_course_external extends external_api {
                                             "users" (int) Include users (default to 0 that is equal to no),
                                             "role_assignments" (int) Include role assignments  (default to 0 that is equal to no),
                                             "comments" (int) Include user comments  (default to 0 that is equal to no),
-                                            "completion_information" (int) Include user course completion information  (default to 0 that is equal to no),
+                                            "userscompletion" (int) Include user course completion information  (default to 0 that is equal to no),
                                             "logs" (int) Include course logs  (default to 0 that is equal to no),
-                                            "histories" (int) Include histories  (default to 0 that is equal to no)'
+                                            "grade_histories" (int) Include histories  (default to 0 that is equal to no)'
                                             ),
                                 'value' => new external_value(PARAM_RAW, 'the value for the option 1 (yes) or 0 (no)'
                             )
@@ -742,9 +742,9 @@ class core_course_external extends external_api {
             'users' => 0,
             'role_assignments' => 0,
             'comments' => 0,
-            'completion_information' => 0,
+            'userscompletion' => 0,
             'logs' => 0,
-            'histories' => 0
+            'grade_histories' => 0
         );
 
         $backupsettings = array();
index 073e76a..391fba7 100644 (file)
@@ -514,13 +514,19 @@ function process_person_tag($tagcontents){
 
 
     // Now if the recstatus is 3, we should delete the user if-and-only-if the setting for delete users is turned on
-    // In the "users" table we can do this by setting deleted=1
     if($recstatus==3){
 
         if($imsdeleteusers){ // If we're allowed to delete user records
-            // Make sure their "deleted" field is set to one
-            $DB->set_field('user', 'deleted', 1, array('username'=>$person->username));
-            $this->log_line("Marked user record for user '$person->username' (ID number $person->idnumber) as deleted.");
+            // Do not dare to hack the user.deleted field directly in database!!!
+            if ($user = $DB->get_record('user', array('username'=>$person->username, 'mnethostid'=>$CFG->mnet_localhost_id, 'deleted'=>0))) {
+                if (delete_user($user)) {
+                    $this->log_line("Deleted user '$person->username' (ID number $person->idnumber).");
+                } else {
+                    $this->log_line("Error deleting '$person->username' (ID number $person->idnumber).");
+                }
+            } else {
+                $this->log_line("Can not delete user '$person->username' (ID number $person->idnumber) - user does not exist.");
+            }
         }else{
             $this->log_line("Ignoring deletion request for user '$person->username' (ID number $person->idnumber).");
         }
@@ -539,8 +545,10 @@ function process_person_tag($tagcontents){
             } else {
 
             // If they don't exist and they have a defined username, and $createnewusers == true, we create them.
-            $person->lang = 'manual'; //TODO: this needs more work due tu multiauth changes
-            $person->auth = $CFG->auth;
+            $person->lang = $CFG->lang;
+            $auth = explode(',', $CFG->auth); //TODO: this needs more work due tu multiauth changes, use first auth for now
+            $auth = reset($auth);
+            $person->auth = $auth;
             $person->confirmed = 1;
             $person->timemodified = time();
             $person->mnethostid = $CFG->mnet_localhost_id;
@@ -568,8 +576,8 @@ function process_person_tag($tagcontents){
         } elseif ($createnewusers) {
             $this->log_line("User record already exists for user '$person->username' (ID number $person->idnumber).");
 
-            // Make sure their "deleted" field is set to zero.
-            $DB->set_field('user', 'deleted', 0, array('idnumber'=>$person->idnumber));
+            // It is totally wrong to mess with deleted users flag directly in database!!!
+            // There is no official way to undelete user, sorry..
         }else{
             $this->log_line("No user record found for '$person->username' (ID number $person->idnumber).");
         }
index e2121d5..3650252 100644 (file)
@@ -93,15 +93,13 @@ class enrol_paypal_edit_form extends moodleform {
 
         list($instance, $plugin, $context) = $this->_customdata;
 
-        if ($data['status'] == ENROL_INSTANCE_ENABLED) {
-            if (!empty($data['enrolenddate']) and $data['enrolenddate'] < $data['enrolstartdate']) {
-                $errors['enrolenddate'] = get_string('enrolenddaterror', 'enrol_paypal');
-            }
+        if (!empty($data['enrolenddate']) and $data['enrolenddate'] < $data['enrolstartdate']) {
+            $errors['enrolenddate'] = get_string('enrolenddaterror', 'enrol_paypal');
+        }
 
-            if (!is_numeric($data['cost'])) {
-                $errors['cost'] = get_string('costerror', 'enrol_paypal');
+        if (!is_numeric($data['cost'])) {
+            $errors['cost'] = get_string('costerror', 'enrol_paypal');
 
-            }
         }
 
         return $errors;
index 74b8c56..0997b72 100644 (file)
@@ -393,16 +393,21 @@ if ($formdata = $mform->get_data()) {
                                 $newgrade->finalgrade = $value;
                             } else {
                                 if ($value === '' or $value == '-') {
-                                    $value = null; // no grade
-
-                                } else if (!is_numeric($value)) {
-                                // non numeric grade value supplied, possibly mapped wrong column
-                                    echo "<br/>t0 is $t0";
-                                    echo "<br/>grade is $value";
-                                    $status = false;
-                                    import_cleanup($importcode);
-                                    echo $OUTPUT->notification(get_string('badgrade', 'grades'));
-                                    break 3;
+                                    $value = null; // No grade.
+                                } else {
+                                    // If the value has a local decimal or can correctly be unformatted, do it.
+                                    $validvalue = unformat_float($value, true);
+                                    if ($validvalue !== false) {
+                                        $value = $validvalue;
+                                    } else {
+                                        // Non numeric grade value supplied, possibly mapped wrong column.
+                                        echo "<br/>t0 is $t0";
+                                        echo "<br/>grade is $value";
+                                        $status = false;
+                                        import_cleanup($importcode);
+                                        echo $OUTPUT->notification(get_string('badgrade', 'grades'));
+                                        break 3;
+                                    }
                                 }
                                 $newgrade->finalgrade = $value;
                             }
diff --git a/install/lang/lb/admin.php b/install/lang/lb/admin.php
new file mode 100644 (file)
index 0000000..7ad5650
--- /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/>.
+
+/**
+ * Automatically generated strings for Moodle installer
+ *
+ * Do not edit this file manually! It contains just a subset of strings
+ * needed during the very first steps of installation. This file was
+ * generated automatically by export-installer.php (which is part of AMOS
+ * {@link http://docs.moodle.org/dev/Languages/AMOS}) using the
+ * list of strings defined in /install/stringnames.txt.
+ *
+ * @package   installer
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+$string['clianswerno'] = 'n';
+$string['cliansweryes'] = 'y';
+$string['cliincorrectvalueerror'] = 'Fehler, falsche Wert "{$a->value}" fir "{$a->option}"';
+$string['cliincorrectvalueretry'] = 'Falsche Wert, probéier nach eng Kéier';
+$string['clitypevalue'] = 'Gëff de Wert an';
+$string['clitypevaluedefault'] = 'Gëff de Wert an, dréck Enter fir de Standardwert ({$a}) ze benotzen';
+$string['cliunknowoption'] = 'Onerkannten Optiounen:
+ {$a}
+Benotz wann ech gelift -- Hëllefsoptioun';
diff --git a/install/lang/lb/langconfig.php b/install/lang/lb/langconfig.php
new file mode 100644 (file)
index 0000000..3cbd689
--- /dev/null
@@ -0,0 +1,33 @@
+<?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/>.
+
+/**
+ * Automatically generated strings for Moodle installer
+ *
+ * Do not edit this file manually! It contains just a subset of strings
+ * needed during the very first steps of installation. This file was
+ * generated automatically by export-installer.php (which is part of AMOS
+ * {@link http://docs.moodle.org/dev/Languages/AMOS}) using the
+ * list of strings defined in /install/stringnames.txt.
+ *
+ * @package   installer
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+$string['thislanguage'] = 'Lëtzebuergesch';
index bf18c4f..28de033 100644 (file)
@@ -31,4 +31,4 @@
 defined('MOODLE_INTERNAL') || die();
 
 $string['environmentrequireinstall'] = 'повинен бути встановлений і включений';
-$string['environmentrequireversion'] = 'потрібна версія {$a->needed} (дійсна версія {$a->current)}';
+$string['environmentrequireversion'] = 'рекомендується версія {$a->needed}, використовується версія {$a->current}';
index b293c5c..55acc20 100644 (file)
@@ -976,40 +976,36 @@ class completion_info {
         }
     }
 
+     /**
+     * Return whether or not the course has activities with completion enabled.
+     *
+     * @return boolean true when there is at least one activity with completion enabled.
+     */
+    public function has_activities() {
+        $modinfo = get_fast_modinfo($this->course);
+        foreach ($modinfo->get_cms() as $cm) {
+            if ($cm->completion != COMPLETION_TRACKING_NONE) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     /**
      * Obtains a list of activities for which completion is enabled on the
      * course. The list is ordered by the section order of those activities.
      *
-     * @param array $modinfo For unit testing only, supply the value
-     *   here. Otherwise the method calls get_fast_modinfo
      * @return array Array from $cmid => $cm of all activities with completion enabled,
      *   empty array if none
      */
-    public function get_activities($modinfo=null) {
-        global $DB;
-
-        // Obtain those activities which have completion turned on
-        $withcompletion = $DB->get_records_select('course_modules', 'course='.$this->course->id.
-          ' AND completion<>'.COMPLETION_TRACKING_NONE);
-        if (!$withcompletion) {
-            return array();
-        }
-
-        // Use modinfo to get section order and also add in names
-        if (empty($modinfo)) {
-            $modinfo = get_fast_modinfo($this->course);
-        }
+    public function get_activities() {
+        $modinfo = get_fast_modinfo($this->course);
         $result = array();
-        foreach ($modinfo->sections as $sectioncms) {
-            foreach ($sectioncms as $cmid) {
-                if (array_key_exists($cmid, $withcompletion)) {
-                    $result[$cmid] = $withcompletion[$cmid];
-                    $result[$cmid]->modname = $modinfo->cms[$cmid]->modname;
-                    $result[$cmid]->name    = $modinfo->cms[$cmid]->name;
-                }
+        foreach ($modinfo->get_cms() as $cm) {
+            if ($cm->completion != COMPLETION_TRACKING_NONE) {
+                $result[$cm->id] = $cm;
             }
         }
-
         return $result;
     }
 
index 1820bea..2d9b2b3 100644 (file)
@@ -1956,7 +1956,11 @@ function readfile_accel($file, $mimetype, $accelerate) {
     header('Last-Modified: '. gmdate('D, d M Y H:i:s', $lastmodified) .' GMT');
 
     if (is_object($file)) {
-        header('ETag: ' . $file->get_contenthash());
+        if (empty($_SERVER['HTTP_RANGE'])) {
+            // Use Etag only when not byteserving,
+            // is it tag of this range or whole file?
+            header('Etag: ' . $file->get_contenthash());
+        }
         if (isset($_SERVER['HTTP_IF_NONE_MATCH']) and $_SERVER['HTTP_IF_NONE_MATCH'] === $file->get_contenthash()) {
             header('HTTP/1.1 304 Not Modified');
             return;
@@ -2606,6 +2610,10 @@ function byteserving_send_file($handle, $mimetype, $ranges, $filesize) {
     // better turn off any kind of compression and buffering
     @ini_set('zlib.output_compression', 'Off');
 
+    // Remove Etag because is is not strictly defined for byteserving,
+    // is it tag of this range or whole file?
+    header_remove('Etag');
+
     $chunksize = 1*(1024*1024); // 1MB chunks - must be less than 2MB!
     if ($handle === false) {
         die;
index 90ba8c2..542be22 100644 (file)
@@ -9404,9 +9404,10 @@ function format_float($float, $decimalpoints=1, $localized=true, $stripzeros=fal
  * Do NOT try to do any math operations before this conversion on any user submitted floats!
  *
  * @param string $locale_float locale aware float representation
- * @return float
+ * @param bool $strict If true, then check the input and return false if it is not a valid number.
+ * @return mixed float|bool - false or the parsed float.
  */
-function unformat_float($locale_float) {
+function unformat_float($locale_float, $strict = false) {
     $locale_float = trim($locale_float);
 
     if ($locale_float == '') {
@@ -9414,8 +9415,13 @@ function unformat_float($locale_float) {
     }
 
     $locale_float = str_replace(' ', '', $locale_float); // no spaces - those might be used as thousand separators
+    $locale_float = str_replace(get_string('decsep', 'langconfig'), '.', $locale_float);
+
+    if ($strict && !is_numeric($locale_float)) {
+        return false;
+    }
 
-    return (float)str_replace(get_string('decsep', 'langconfig'), '.', $locale_float);
+    return (float)$locale_float;
 }
 
 /**
index 25e2c10..07eea13 100644 (file)
@@ -889,8 +889,9 @@ class navigation_node_collection implements IteratorAggregate {
         $child = $this->get($key, $type);
         if ($child !== false) {
             foreach ($this->collection as $colkey => $node) {
-                if ($node->key == $key && $node->type == $type) {
+                if ($node->key === $key && $node->type == $type) {
                     unset($this->collection[$colkey]);
+                    $this->collection = array_values($this->collection);
                     break;
                 }
             }
diff --git a/lib/tests/completionlib_advanced_test.php b/lib/tests/completionlib_advanced_test.php
new file mode 100644 (file)
index 0000000..e259554
--- /dev/null
@@ -0,0 +1,91 @@
+<?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/>.
+
+/**
+ * Completion lib advanced test case.
+ *
+ * This file contains the advanced test suite for completion lib.
+ *
+ * @package    core_completion
+ * @category   phpunit
+ * @copyright  2013 Frédéric Massart
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+global $CFG;
+require_once($CFG->libdir.'/completionlib.php');
+
+class completionlib_advanced_testcase extends advanced_testcase {
+
+    function test_get_activities() {
+        global $DB;
+        $this->resetAfterTest(true);
+
+        // Create a course with mixed auto completion data.
+        $course = $this->getDataGenerator()->create_course();
+        $completionauto = array('completion' => COMPLETION_TRACKING_AUTOMATIC);
+        $completionmanual = array('completion' => COMPLETION_TRACKING_MANUAL);
+        $completionnone = array('completion' => COMPLETION_TRACKING_NONE);
+        $forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id), $completionauto);
+        $page = $this->getDataGenerator()->create_module('page', array('course' => $course->id), $completionauto);
+        $data = $this->getDataGenerator()->create_module('data', array('course' => $course->id), $completionmanual);
+
+        $forum2 = $this->getDataGenerator()->create_module('forum', array('course' => $course->id), $completionnone);
+        $page2 = $this->getDataGenerator()->create_module('page', array('course' => $course->id), $completionnone);
+        $data2 = $this->getDataGenerator()->create_module('data', array('course' => $course->id), $completionnone);
+
+        // Create data in another course to make sure it's not considered.
+        $course2 = $this->getDataGenerator()->create_course();
+        $c2forum = $this->getDataGenerator()->create_module('forum', array('course' => $course2->id), $completionauto);
+        $c2page = $this->getDataGenerator()->create_module('page', array('course' => $course2->id), $completionmanual);
+        $c2data = $this->getDataGenerator()->create_module('data', array('course' => $course2->id), $completionnone);
+
+        $c = new completion_info($course);
+        $activities = $c->get_activities();
+        $this->assertEquals(3, count($activities));
+        $this->assertTrue(isset($activities[$forum->cmid]));
+        $this->assertEquals($activities[$forum->cmid]->name, $forum->name);
+        $this->assertTrue(isset($activities[$page->cmid]));
+        $this->assertEquals($activities[$page->cmid]->name, $page->name);
+        $this->assertTrue(isset($activities[$data->cmid]));
+        $this->assertEquals($activities[$data->cmid]->name, $data->name);
+
+        $this->assertFalse(isset($activities[$forum2->cmid]));
+        $this->assertFalse(isset($activities[$page2->cmid]));
+        $this->assertFalse(isset($activities[$data2->cmid]));
+    }
+
+    function test_has_activities() {
+        global $DB;
+        $this->resetAfterTest(true);
+
+        // Create a course with mixed auto completion data.
+        $course = $this->getDataGenerator()->create_course();
+        $course2 = $this->getDataGenerator()->create_course();
+        $completionauto = array('completion' => COMPLETION_TRACKING_AUTOMATIC);
+        $completionnone = array('completion' => COMPLETION_TRACKING_NONE);
+        $c1forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id), $completionauto);
+        $c2forum = $this->getDataGenerator()->create_module('forum', array('course' => $course2->id), $completionnone);
+
+        $c1 = new completion_info($course);
+        $c2 = new completion_info($course2);
+
+        $this->assertTrue($c1->has_activities());
+        $this->assertFalse($c2->has_activities());
+    }
+}
index 5092831..2298460 100644 (file)
@@ -514,31 +514,6 @@ WHERE
         $c->internal_set_data($cm, $data);
     }
 
-    function test_get_activities() {
-        global $DB;
-
-        $c = new completion_info((object)array('id'=>42));
-
-        // Try with no activities
-        $DB->expects($this->at(0))
-            ->method('get_records_select')
-            ->with('course_modules', 'course=42 AND completion<>'.COMPLETION_TRACKING_NONE)
-            ->will($this->returnValue(array()));
-        $result = $c->get_activities();
-        $this->assertEquals(array(), $result);
-
-        // Try with an activity (need to fake up modinfo for it as well)
-        $DB->expects($this->at(0))
-            ->method('get_records_select')
-            ->with('course_modules', 'course=42 AND completion<>'.COMPLETION_TRACKING_NONE)
-            ->will($this->returnValue(array(13=>(object)array('id'=>13))));
-        $modinfo = new stdClass;
-        $modinfo->sections = array(array(1, 2, 3), array(12, 13, 14));
-        $modinfo->cms[13] = (object)array('modname'=>'frog', 'name'=>'kermit');
-        $result = $c->get_activities($modinfo);
-        $this->assertEquals(array(13=>(object)array('id'=>13, 'modname'=>'frog', 'name'=>'kermit')), $result);
-    }
-
     // get_tracked_users() cannot easily be tested because it uses
     // get_role_users, so skipping that
 
diff --git a/lib/tests/environment_test.php b/lib/tests/environment_test.php
new file mode 100644 (file)
index 0000000..9dea45d
--- /dev/null
@@ -0,0 +1,45 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Moodle environment test.
+ *
+ * @package    core
+ * @category   phpunit
+ * @copyright  2013 Petr Skoda {@link http://skodak.org}
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+
+/**
+ * Do standard environment.xml tests.
+ */
+class environment_testcase extends advanced_testcase {
+
+    public function test_environment() {
+        global $CFG;
+
+        require_once($CFG->libdir.'/environmentlib.php');
+        list($envstatus, $environment_results) = check_moodle_environment(normalize_version($CFG->release), ENV_SELECT_RELEASE);
+
+        $this->assertNotEmpty($envstatus);
+        foreach ($environment_results as $environment_result) {
+            $this->assertTrue($environment_result->getStatus(), "Problem detected in environment ($environment_result->part:$environment_result->info), fix all warnings and errors!");
+        }
+    }
+}
index 09999c7..e4608da 100644 (file)
@@ -83,6 +83,29 @@ class moodlelib_testcase extends advanced_testcase {
         )
     );
 
+    /**
+     * Define a local decimal separator.
+     *
+     * It is not possible to directly change the result of get_string in
+     * a unit test. Instead, we create a language pack for language 'xx' in
+     * dataroot and make langconfig.php with the string we need to change.
+     * The example separator used here is 'X'; on PHP 5.3 and before this
+     * must be a single byte character due to PHP bug/limitation in
+     * number_format, so you can't use UTF-8 characters.
+     *
+     * @global type $SESSION
+     * @global type $CFG
+     */
+    protected function define_local_decimal_separator() {
+        global $SESSION, $CFG;
+
+        $SESSION->lang = 'xx';
+        $langconfig = "<?php\n\$string['decsep'] = 'X';";
+        $langfolder = $CFG->dataroot . '/lang/xx';
+        check_dir_exists($langfolder);
+        file_put_contents($langfolder . '/langconfig.php', $langconfig);
+    }
+
     function test_cleanremoteaddr() {
         //IPv4
         $this->assertEquals(cleanremoteaddr('1023.121.234.1'), null);
@@ -2119,7 +2142,6 @@ class moodlelib_testcase extends advanced_testcase {
      * Test localised float formatting.
      */
     public function test_format_float() {
-        global $SESSION, $CFG;
 
         // Special case for null
         $this->assertEquals('', format_float(null));
@@ -2135,17 +2157,8 @@ class moodlelib_testcase extends advanced_testcase {
         $this->assertEquals('5.43', format_float(5.43, 5, true, true));
         $this->assertEquals('5', format_float(5.0001, 3, true, true));
 
-        // It is not possible to directly change the result of get_string in
-        // a unit test. Instead, we create a language pack for language 'xx' in
-        // dataroot and make langconfig.php with the string we need to change.
-        // The example separator used here is 'X'; on PHP 5.3 and before this
-        // must be a single byte character due to PHP bug/limitation in
-        // number_format, so you can't use UTF-8 characters.
-        $SESSION->lang = 'xx';
-        $langconfig = "<?php\n\$string['decsep'] = 'X';";
-        $langfolder = $CFG->dataroot . '/lang/xx';
-        check_dir_exists($langfolder);
-        file_put_contents($langfolder . '/langconfig.php', $langconfig);
+        // Tests with a localised decimal separator.
+        $this->define_local_decimal_separator();
 
         // Localisation on (default)
         $this->assertEquals('5X43000', format_float(5.43, 5));
@@ -2156,6 +2169,80 @@ class moodlelib_testcase extends advanced_testcase {
         $this->assertEquals('5.43', format_float(5.43, 5, false, true));
     }
 
+    /**
+     * Test localised float unformatting.
+     */
+    public function test_unformat_float() {
+
+        // Tests without the localised decimal separator.
+
+        // Special case for null, empty or white spaces only strings.
+        $this->assertEquals(null, unformat_float(null));
+        $this->assertEquals(null, unformat_float(''));
+        $this->assertEquals(null, unformat_float('    '));
+
+        // Regular use.
+        $this->assertEquals(5.4, unformat_float('5.4'));
+        $this->assertEquals(5.4, unformat_float('5.4', true));
+
+        // No decimal.
+        $this->assertEquals(5.0, unformat_float('5'));
+
+        // Custom number of decimal.
+        $this->assertEquals(5.43267, unformat_float('5.43267'));
+
+        // Empty decimal.
+        $this->assertEquals(100.0, unformat_float('100.00'));
+
+        // With the thousand separator.
+        $this->assertEquals(1000.0, unformat_float('1 000'));
+        $this->assertEquals(1000.32, unformat_float('1 000.32'));
+
+        // Negative number.
+        $this->assertEquals(-100.0, unformat_float('-100'));
+
+        // Wrong value.
+        $this->assertEquals(0.0, unformat_float('Wrong value'));
+        // Wrong value in strict mode.
+        $this->assertFalse(unformat_float('Wrong value', true));
+
+        // Combining options.
+        $this->assertEquals(-1023.862567, unformat_float('   -1 023.862567     '));
+
+        // Bad decimal separator (should crop the decimal).
+        $this->assertEquals(50.0, unformat_float('50,57'));
+        // Bad decimal separator in strict mode (should return false).
+        $this->assertFalse(unformat_float('50,57', true));
+
+        // Tests with a localised decimal separator.
+        $this->define_local_decimal_separator();
+
+        // We repeat the tests above but with the current decimal separator.
+
+        // Regular use without and with the localised separator.
+        $this->assertEquals (5.4, unformat_float('5.4'));
+        $this->assertEquals (5.4, unformat_float('5X4'));
+
+        // Custom number of decimal.
+        $this->assertEquals (5.43267, unformat_float('5X43267'));
+
+        // Empty decimal.
+        $this->assertEquals (100.0, unformat_float('100X00'));
+
+        // With the thousand separator.
+        $this->assertEquals (1000.32, unformat_float('1 000X32'));
+
+        // Bad different separator (should crop the decimal).
+        $this->assertEquals (50.0, unformat_float('50Y57'));
+        // Bad different separator in strict mode (should return false).
+        $this->assertFalse (unformat_float('50Y57', true));
+
+        // Combining options.
+        $this->assertEquals (-1023.862567, unformat_float('   -1 023X862567     '));
+        // Combining options in strict mode.
+        $this->assertEquals (-1023.862567, unformat_float('   -1 023X862567     ', true));
+    }
+
     /**
      * Test deleting of users.
      */
index 63804f7..473b659 100644 (file)
@@ -60,6 +60,8 @@ class navigation_node_testcase extends basic_testcase {
 
         $this->node = new navigation_node('Test Node');
         $this->node->type = navigation_node::TYPE_SYSTEM;
+        // We add the first child without key. This way we make sure all keys search by comparision is performed using ===
+        $this->node->add('first child without key', null, navigation_node::TYPE_CUSTOM);
         $demo1 = $this->node->add('demo1', $this->inactiveurl, navigation_node::TYPE_COURSE, null, 'demo1', new pix_icon('i/course', ''));
         $demo2 = $this->node->add('demo2', $this->inactiveurl, navigation_node::TYPE_COURSE, null, 'demo2', new pix_icon('i/course', ''));
         $demo3 = $this->node->add('demo3', $this->inactiveurl, navigation_node::TYPE_CATEGORY, null, 'demo3',new pix_icon('i/course', ''));
@@ -251,8 +253,24 @@ class navigation_node_testcase extends basic_testcase {
         $this->assertInstanceOf('navigation_node', $this->node->get('remove2'));
         $this->assertInstanceOf('navigation_node', $remove2->get('remove3'));
 
+        // Remove element and make sure this is no longer a child.
         $this->assertTrue($remove1->remove());
+        $this->assertFalse($this->node->get('remove1'));
+        $this->assertFalse(in_array('remove1', $this->node->get_children_key_list(), true));
+
+        // Make sure that we can insert element after removal
+        $insertelement = navigation_node::create('extra element 4', null, navigation_node::TYPE_CUSTOM, null, 'element4');
+        $this->node->add_node($insertelement, 'remove2');
+        $this->assertNotEmpty($this->node->get('element4'));
+
+        // Remove more elements
         $this->assertTrue($this->node->get('remove2')->remove());
+        $this->assertFalse($this->node->get('remove2'));
+
+        // Make sure that we can add element after removal
+        $this->node->add('extra element 5', null, navigation_node::TYPE_CUSTOM, null, 'element5');
+        $this->assertNotEmpty($this->node->get('element5'));
+
         $this->assertTrue($remove2->get('remove3')->remove());
 
         $this->assertFalse($this->node->get('remove1'));
index 3a563fd..5c25485 100644 (file)
@@ -68,6 +68,12 @@ function xsendfile($filepath) {
         }
     }
 
+    // Remove Etag because is is not strictly defined for byteserving,
+    // is it tag of this range or whole file?
+    if (!empty($_SERVER['HTTP_RANGE'])) {
+        header_remove('Etag');
+    }
+
     if ($CFG->xsendfile === 'X-LIGHTTPD-send-file') {
         // http://redmine.lighttpd.net/projects/lighttpd/wiki/X-LIGHTTPD-send-file says 1.4 it does not support byteserving
         header('Accept-Ranges: none');
index a7da827..39b2e64 100644 (file)
@@ -106,15 +106,17 @@ class assign_grading_table extends table_sql implements renderable {
         // The filters do not make sense when there are no submissions, so do not apply them.
         if ($this->assignment->is_any_submission_plugin_enabled()) {
             if ($filter == ASSIGN_FILTER_SUBMITTED) {
-                $where .= ' AND s.timecreated > 0 ';
-            }
-            if ($filter == ASSIGN_FILTER_REQUIRE_GRADING) {
+                $where .= ' AND (s.timemodified IS NOT NULL AND
+                                 s.status = :submitted) ';
+                $params['submitted'] = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
+
+            } else if ($filter == ASSIGN_FILTER_REQUIRE_GRADING) {
                 $where .= ' AND (s.timemodified IS NOT NULL AND
                                  s.status = :submitted AND
                                  (s.timemodified > g.timemodified OR g.timemodified IS NULL))';
                 $params['submitted'] = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
-            }
-            if (strpos($filter, ASSIGN_FILTER_SINGLE_USER) === 0) {
+
+            } else if (strpos($filter, ASSIGN_FILTER_SINGLE_USER) === 0) {
                 $userfilter = (int) array_pop(explode('=', $filter));
                 $where .= ' AND (u.id = :userid)';
                 $params['userid'] = $userfilter;
index e97c2e5..d6e7c41 100644 (file)
@@ -2352,7 +2352,7 @@ class assign {
             return false;
         }
         $assign = clone $this->get_instance();
-        $assign->cmidnumber = $this->get_course_module()->id;
+        $assign->cmidnumber = $this->get_course_module()->idnumber;
 
         return assign_grade_item_update($assign, $gradebookgrade);
     }
index be060c0..4f07f8e 100644 (file)
@@ -70,6 +70,7 @@ require_login($course, true, $cm);
 require_capability('mod/feedback:mapcourse', $context);
 
 if ($coursefilter) {
+    $map = new stdClass;
     $map->feedbackid = $feedback->id;
     $map->courseid = $coursefilter;
     // insert a map only if it does exists yet
@@ -125,6 +126,7 @@ echo '</form>';
 
 if ($coursemap = feedback_get_courses_from_sitecourse_map($feedback->id)) {
     $table = new flexible_table('coursemaps');
+    $table->baseurl = $url;
     $table->define_columns( array('course'));
     $table->define_headers( array(get_string('mappedcourses', 'feedback')));
 
@@ -132,9 +134,8 @@ if ($coursemap = feedback_get_courses_from_sitecourse_map($feedback->id)) {
 
     $unmapurl = new moodle_url('/mod/feedback/unmapcourse.php');
     foreach ($coursemap as $cmap) {
-        $cmapcontext = get_context_instance(CONTEXT_COURSE, $cmap->id);
-        $cmapshortname = format_string($cmap->shortname, true, array('context' => $cmapcontext));
-        $coursecontext = get_context_instance(CONTEXT_COURSE, $cmap->courseid);
+        $coursecontext = context_course::instance($cmap->courseid);
+        $cmapshortname = format_string($cmap->shortname, true, array('context' => $coursecontext));
         $cmapfullname = format_string($cmap->fullname, true, array('context' => $coursecontext));
         $unmapurl->params(array('id'=>$id, 'cmapid'=>$cmap->id));
         $anker = '<a href="'.$unmapurl->out().'">';
index 6286973..7a03152 100644 (file)
@@ -23,5 +23,4 @@
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
-$string['pluginname'] = 'Graph report';
-
+$string['pluginname'] = 'Basic report';
index 9fdfc03..c9fb2ca 100644 (file)
@@ -757,16 +757,16 @@ abstract class question_utils {
     public static function arrays_same_at_key_integer(
             array $array1, array $array2, $key) {
         if (array_key_exists($key, $array1)) {
-            $value1 = $array1[$key];
+            $value1 = (int) $array1[$key];
         } else {
             $value1 = 0;
         }
         if (array_key_exists($key, $array2)) {
-            $value2 = $array2[$key];
+            $value2 = (int) $array2[$key];
         } else {
             $value2 = 0;
         }
-        return ((integer) $value1) === ((integer) $value2);
+        return $value1 === $value2;
     }
 
     private static $units     = array('', 'i', 'ii', 'iii', 'iv', 'v', 'vi', 'vii', 'viii', 'ix');
index 8dd7086..67388b6 100644 (file)
@@ -338,7 +338,7 @@ class qtype_multichoice_multi_question extends qtype_multichoice_base {
     public function is_same_response(array $prevresponse, array $newresponse) {
         foreach ($this->order as $key => $notused) {
             $fieldname = $this->field($key);
-            if (!question_utils::arrays_same_at_key($prevresponse, $newresponse, $fieldname)) {
+            if (!question_utils::arrays_same_at_key_integer($prevresponse, $newresponse, $fieldname)) {
                 return false;
             }
         }
diff --git a/question/type/multichoice/tests/question_multi_test.php b/question/type/multichoice/tests/question_multi_test.php
new file mode 100644 (file)
index 0000000..11fe16f
--- /dev/null
@@ -0,0 +1,152 @@
+<?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/>.
+
+/**
+ * Unit tests for the multiple choice, multi-response question definition classes.
+ *
+ * @package   qtype_multichoice
+ * @copyright 2009 The Open University
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+global $CFG;
+require_once($CFG->dirroot . '/question/engine/tests/helpers.php');
+
+
+/**
+ * Unit tests for the multiple choice, multi-response question definition class.
+ *
+ * @copyright 2009 The Open University
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class qtype_multichoice_multi_question_test extends advanced_testcase {
+
+    public function test_get_expected_data() {
+        $question = test_question_maker::make_a_multichoice_multi_question();
+        $question->start_attempt(new question_attempt_step(), 1);
+
+        $this->assertEquals(array('choice0' => PARAM_BOOL, 'choice1' => PARAM_BOOL,
+                'choice2' => PARAM_BOOL, 'choice3' => PARAM_BOOL), $question->get_expected_data());
+    }
+
+    public function test_is_complete_response() {
+        $question = test_question_maker::make_a_multichoice_multi_question();
+        $question->start_attempt(new question_attempt_step(), 1);
+
+        $this->assertFalse($question->is_complete_response(array()));
+        $this->assertFalse($question->is_complete_response(
+                array('choice0' => '0', 'choice1' => '0', 'choice2' => '0', 'choice3' => '0')));
+        $this->assertTrue($question->is_complete_response(array('choice1' => '1')));
+        $this->assertTrue($question->is_complete_response(
+                array('choice0' => '1', 'choice1' => '1', 'choice2' => '1', 'choice3' => '1')));
+    }
+
+    public function test_is_gradable_response() {
+        $question = test_question_maker::make_a_multichoice_multi_question();
+        $question->start_attempt(new question_attempt_step(), 1);
+
+        $this->assertFalse($question->is_gradable_response(array()));
+        $this->assertFalse($question->is_gradable_response(
+                array('choice0' => '0', 'choice1' => '0', 'choice2' => '0', 'choice3' => '0')));
+        $this->assertTrue($question->is_gradable_response(array('choice1' => '1')));
+        $this->assertTrue($question->is_gradable_response(
+                array('choice0' => '1', 'choice1' => '1', 'choice2' => '1', 'choice3' => '1')));
+    }
+
+    public function test_is_same_response() {
+        $question = test_question_maker::make_a_multichoice_multi_question();
+        $question->start_attempt(new question_attempt_step(), 1);
+
+        $this->assertTrue($question->is_same_response(
+                array(),
+                array('choice0' => '0', 'choice1' => '0', 'choice2' => '0', 'choice3' => '0')));
+
+        $this->assertTrue($question->is_same_response(
+                array('choice0' => '0', 'choice1' => '0', 'choice2' => '0', 'choice3' => '0'),
+                array('choice0' => '0', 'choice1' => '0', 'choice2' => '0', 'choice3' => '0')));
+
+        $this->assertFalse($question->is_same_response(
+                array('choice0' => '0', 'choice1' => '0', 'choice2' => '0', 'choice3' => '0'),
+                array('choice0' => '1', 'choice1' => '0', 'choice2' => '0', 'choice3' => '0')));
+
+        $this->assertTrue($question->is_same_response(
+                array('choice0' => '1', 'choice1' => '0', 'choice2' => '1', 'choice3' => '0'),
+                array('choice0' => '1', 'choice1' => '0', 'choice2' => '1', 'choice3' => '0')));
+    }
+
+    public function test_grading() {
+        $question = test_question_maker::make_a_multichoice_multi_question();
+        $question->shuffleanswers = false;
+        $question->start_attempt(new question_attempt_step(), 1);
+
+        $this->assertEquals(array(1, question_state::$gradedright),
+                $question->grade_response(array('choice0' => '1', 'choice2' => '1')));
+        $this->assertEquals(array(0.5, question_state::$gradedpartial),
+                $question->grade_response(array('choice0' => '1')));
+        $this->assertEquals(array(0, question_state::$gradedwrong),
+                $question->grade_response(
+                        array('choice0' => '1', 'choice1' => '1', 'choice2' => '1')));
+        $this->assertEquals(array(0, question_state::$gradedwrong),
+                $question->grade_response(array('choice1' => '1')));
+    }
+
+    public function test_get_correct_response() {
+        $question = test_question_maker::make_a_multichoice_multi_question();
+        $question->shuffleanswers = false;
+        $question->start_attempt(new question_attempt_step(), 1);
+
+        $this->assertEquals(array('choice0' => '1', 'choice2' => '1'),
+                $question->get_correct_response());
+    }
+
+    public function test_get_question_summary() {
+        $mc = test_question_maker::make_a_multichoice_single_question();
+        $mc->start_attempt(new question_attempt_step(), 1);
+
+        $qsummary = $mc->get_question_summary();
+
+        $this->assertRegExp('/' . preg_quote($mc->questiontext, '/') . '/', $qsummary);
+        foreach ($mc->answers as $answer) {
+            $this->assertRegExp('/' . preg_quote($answer->answer, '/') . '/', $qsummary);
+        }
+    }
+
+    public function test_summarise_response() {
+        $mc = test_question_maker::make_a_multichoice_multi_question();
+        $mc->shuffleanswers = false;
+        $mc->start_attempt(new question_attempt_step(), 1);
+
+        $summary = $mc->summarise_response(array('choice1' => 1, 'choice2' => 1),
+                test_question_maker::get_a_qa($mc));
+
+        $this->assertEquals('B; C', $summary);
+    }
+
+    public function test_classify_response() {
+        $mc = test_question_maker::make_a_multichoice_multi_question();
+        $mc->shuffleanswers = false;
+        $mc->start_attempt(new question_attempt_step(), 1);
+
+        $this->assertEquals(array(
+                    13 => new question_classified_response(13, 'A', 0.5),
+                    14 => new question_classified_response(14, 'B', -1.0),
+                ), $mc->classify_response(array('choice0' => 1, 'choice1' => 1)));
+
+        $this->assertEquals(array(), $mc->classify_response(array()));
+    }
+}
 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
 /**
- * Unit tests for the multiple choice question definition classes.
+ * Unit tests for the multiple choice, single response question definition classes.
  *
- * @package    qtype
- * @subpackage multichoice
- * @copyright  2009 The Open University
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @package   qtype_multichoice
+ * @copyright 2009 The Open University
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
-
 defined('MOODLE_INTERNAL') || die();
 
 global $CFG;
@@ -31,10 +29,10 @@ require_once($CFG->dirroot . '/question/engine/tests/helpers.php');
 
 
 /**
- * Unit tests for the multiple choice, multiple response question definition class.
+ * Unit tests for the multiple choice, single response question definition class.
  *
- * @copyright  2009 The Open University
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @copyright 2009 The Open University
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 class qtype_multichoice_single_question_test extends advanced_testcase {
 
@@ -59,6 +57,31 @@ class qtype_multichoice_single_question_test extends advanced_testcase {
         $this->assertTrue($question->is_gradable_response(array('answer' => '2')));
     }
 
+    public function test_is_same_response() {
+        $question = test_question_maker::make_a_multichoice_single_question();
+        $question->start_attempt(new question_attempt_step(), 1);
+
+        $this->assertTrue($question->is_same_response(
+                array(),
+                array()));
+
+        $this->assertFalse($question->is_same_response(
+                array(),
+                array('answer' => '0')));
+
+        $this->assertTrue($question->is_same_response(
+                array('answer' => '0'),
+                array('answer' => '0')));
+
+        $this->assertFalse($question->is_same_response(
+                array('answer' => '0'),
+                array('answer' => '1')));
+
+        $this->assertTrue($question->is_same_response(
+                array('answer' => '2'),
+                array('answer' => '2')));
+    }
+
     public function test_grading() {
         $question = test_question_maker::make_a_multichoice_single_question();
         $question->shuffleanswers = false;
@@ -151,106 +174,3 @@ class qtype_multichoice_single_question_test extends advanced_testcase {
         $this->assertEquals('Frog<br />†', $mc->make_html_inline('<p>Frog</p><p>†</p>'));
     }
 }
-
-
-/**
- * Unit tests for the multiple choice, single response question definition class.
- *
- * @copyright  2009 The Open University
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-class qtype_multichoice_multi_question_test extends advanced_testcase {
-
-    public function test_get_expected_data() {
-        $question = test_question_maker::make_a_multichoice_multi_question();
-        $question->start_attempt(new question_attempt_step(), 1);
-
-        $this->assertEquals(array('choice0' => PARAM_BOOL, 'choice1' => PARAM_BOOL,
-                'choice2' => PARAM_BOOL, 'choice3' => PARAM_BOOL), $question->get_expected_data());
-    }
-
-    public function test_is_complete_response() {
-        $question = test_question_maker::make_a_multichoice_multi_question();
-        $question->start_attempt(new question_attempt_step(), 1);
-
-        $this->assertFalse($question->is_complete_response(array()));
-        $this->assertFalse($question->is_complete_response(
-                array('choice0' => '0', 'choice1' => '0', 'choice2' => '0', 'choice3' => '0')));
-        $this->assertTrue($question->is_complete_response(array('choice1' => '1')));
-        $this->assertTrue($question->is_complete_response(
-                array('choice0' => '1', 'choice1' => '1', 'choice2' => '1', 'choice3' => '1')));
-    }
-
-    public function test_is_gradable_response() {
-        $question = test_question_maker::make_a_multichoice_multi_question();
-        $question->start_attempt(new question_attempt_step(), 1);
-
-        $this->assertFalse($question->is_gradable_response(array()));
-        $this->assertFalse($question->is_gradable_response(
-                array('choice0' => '0', 'choice1' => '0', 'choice2' => '0', 'choice3' => '0')));
-        $this->assertTrue($question->is_gradable_response(array('choice1' => '1')));
-        $this->assertTrue($question->is_gradable_response(
-                array('choice0' => '1', 'choice1' => '1', 'choice2' => '1', 'choice3' => '1')));
-    }
-
-    public function test_grading() {
-        $question = test_question_maker::make_a_multichoice_multi_question();
-        $question->shuffleanswers = false;
-        $question->start_attempt(new question_attempt_step(), 1);
-
-        $this->assertEquals(array(1, question_state::$gradedright),
-                $question->grade_response(array('choice0' => '1', 'choice2' => '1')));
-        $this->assertEquals(array(0.5, question_state::$gradedpartial),
-                $question->grade_response(array('choice0' => '1')));
-        $this->assertEquals(array(0, question_state::$gradedwrong),
-                $question->grade_response(
-                        array('choice0' => '1', 'choice1' => '1', 'choice2' => '1')));
-        $this->assertEquals(array(0, question_state::$gradedwrong),
-                $question->grade_response(array('choice1' => '1')));
-    }
-
-    public function test_get_correct_response() {
-        $question = test_question_maker::make_a_multichoice_multi_question();
-        $question->shuffleanswers = false;
-        $question->start_attempt(new question_attempt_step(), 1);
-
-        $this->assertEquals(array('choice0' => '1', 'choice2' => '1'),
-                $question->get_correct_response());
-    }
-
-    public function test_get_question_summary() {
-        $mc = test_question_maker::make_a_multichoice_single_question();
-        $mc->start_attempt(new question_attempt_step(), 1);
-
-        $qsummary = $mc->get_question_summary();
-
-        $this->assertRegExp('/' . preg_quote($mc->questiontext) . '/', $qsummary);
-        foreach ($mc->answers as $answer) {
-            $this->assertRegExp('/' . preg_quote($answer->answer) . '/', $qsummary);
-        }
-    }
-
-    public function test_summarise_response() {
-        $mc = test_question_maker::make_a_multichoice_multi_question();
-        $mc->shuffleanswers = false;
-        $mc->start_attempt(new question_attempt_step(), 1);
-
-        $summary = $mc->summarise_response(array('choice1' => 1, 'choice2' => 1),
-                test_question_maker::get_a_qa($mc));
-
-        $this->assertEquals('B; C', $summary);
-    }
-
-    public function test_classify_response() {
-        $mc = test_question_maker::make_a_multichoice_multi_question();
-        $mc->shuffleanswers = false;
-        $mc->start_attempt(new question_attempt_step(), 1);
-
-        $this->assertEquals(array(
-                    13 => new question_classified_response(13, 'A', 0.5),
-                    14 => new question_classified_response(14, 'B', -1.0),
-                ), $mc->classify_response(array('choice0' => 1, 'choice1' => 1)));
-
-        $this->assertEquals(array(), $mc->classify_response(array()));
-    }
-}
index c8ec1e4..3931e71 100644 (file)
@@ -44,7 +44,7 @@ function report_progress_extend_navigation_course($navigation, $course, $context
     }
 
     $completion = new completion_info($course);
-    $showonnavigation = ($showonnavigation && $completion->is_enabled() && count($completion->get_activities())>0);
+    $showonnavigation = ($showonnavigation && $completion->is_enabled() && $completion->has_activities());
     if ($showonnavigation) {
         $url = new moodle_url('/report/progress/index.php', array('course'=>$course->id));
         $navigation->add(get_string('pluginname','report_progress'), $url, navigation_node::TYPE_SETTING, null, null, new pix_icon('i/report', ''));
index c599e14..9aaaa09 100644 (file)
@@ -186,7 +186,7 @@ if (!empty($edit) || !empty($new)) {
             $settings = array();
             $settings['name'] = $fromform->name;
             foreach($configs as $config) {
-                $settings[$config] = $fromform->$config;
+                $settings[$config] = isset($fromform->$config) ? $fromform->$config : null;
             }
             $success = $instance->set_option($settings);
         } else {
diff --git a/theme/sky_high/pix/footer-rtl.png b/theme/sky_high/pix/footer-rtl.png
new file mode 100644 (file)
index 0000000..7ba3703
Binary files /dev/null and b/theme/sky_high/pix/footer-rtl.png differ
index 34365a8..f2d649c 100644 (file)
Binary files a/theme/sky_high/pix/footer.png and b/theme/sky_high/pix/footer.png differ
index 949de96..253d320 100644 (file)
@@ -7,6 +7,7 @@ body.pagelayout-admin.has_dock {
     width: 100%;
     margin: 0 auto;
     position: relative;
+    max-width: 1664px;
 }
 .pagelayout-admin #page-footer {
     float: none;
index d243e8e..644fb49 100644 (file)
@@ -10,9 +10,9 @@ body {
 }
 
 #page {
-    width:100%;
-    max-width: 1600px;
-    margin:0 auto;
+    width: 100%;
+    max-width: 1760px;
+    margin: 0 auto;
 }
 
 #page-content {
index a9bdbb3..0859374 100644 (file)
@@ -1,5 +1,8 @@
 /** Report layout **/
 
+.pagelayout-report #page {
+    max-width: 1664px;
+}
 #report-wrapper {
     background: #fff;
     margin: 20px 2% 0;
index 9d898ed..d676d02 100644 (file)
 defined('MOODLE_INTERNAL') || die();
 
 
-$version  = 2012062507.02;              // YYYYMMDD      = weekly release date of this DEV branch
+$version  = 2012062507.03;              // YYYYMMDD      = weekly release date of this DEV branch
                                         //         RR    = release increments - 00 in DEV branches
                                         //           .XX = incremental changes
 
-$release  = '2.3.7+ (Build: 20130524)';  // Human-friendly version name
+$release  = '2.3.7+ (Build: 20130530)';  // Human-friendly version name
 
 $branch   = '23';                       // this version's branch
 $maturity = MATURITY_STABLE;            // this version's maturity level