Merge branch 'wip-MDL-25602-master' of git://github.com/samhemelryk/moodle
authorPetr Skoda <commits@skodak.org>
Mon, 15 Aug 2011 18:45:43 +0000 (20:45 +0200)
committerPetr Skoda <commits@skodak.org>
Mon, 15 Aug 2011 18:45:43 +0000 (20:45 +0200)
grade/import/csv/index.php
mod/scorm/locallib.php
question/format.php
question/format/aiken/format.php
question/format/aiken/simpletest/fixtures/questions.aiken.txt [new file with mode: 0644]
rating/lib.php
rating/simpletest/testrating.php [new file with mode: 0644]
repository/local/lib.php

index 8681a83..04844b0 100644 (file)
@@ -481,7 +481,7 @@ if ($formdata = $mform->get_data()) {
         fclose($fp);
         unlink($filename);
     } else {
-        print_error('cannotreadfil');
+        print_error('cannotreadfile');
     }
 
 } else {
index 51248fd..fd09321 100644 (file)
@@ -1288,9 +1288,6 @@ function scorm_get_toc($user,$scorm,$cmid,$toclink=TOCJSLINK,$currentorg='',$sco
         $usertracks = array();
         foreach ($scoes as $sco) {
             if (!empty($sco->launch)) {
-                if (empty($scoid)) {
-                    $scoid = $sco->id;
-                }
                 if ($usertrack = scorm_get_tracks($sco->id,$user->id,$attempt)) {
                     if ($usertrack->status == '') {
                         $usertrack->status = 'notattempted';
@@ -1464,10 +1461,10 @@ function scorm_get_toc($user,$scorm,$cmid,$toclink=TOCJSLINK,$currentorg='',$sco
         }
 
         if ($play) {
-            if (empty($scoid)) {
-                $scoid = reset($scoes)->id;
+            // it is possible that $scoid is still not set, in this case we don't want an empty object
+            if ($scoid) {
+                $sco = scorm_get_sco($scoid);
             }
-            $sco = scorm_get_sco($scoid);
             $sco->previd = $previd;
             $sco->nextid = $nextid;
             $result->sco = $sco;
index 8cdbfbd..dc3db4a 100644 (file)
@@ -394,6 +394,8 @@ class qformat_default {
 
             $question->createdby = $USER->id;
             $question->timecreated = time();
+            $question->modifiedby = $USER->id;
+            $question->timemodified = time();
 
             $question->id = $DB->insert_record('question', $question);
             if (isset($question->questiontextfiles)) {
@@ -595,7 +597,9 @@ class qformat_default {
         $question->image = "";
         $question->usecase = 0;
         $question->multiplier = array();
+        $question->questiontextformat = FORMAT_MOODLE;
         $question->generalfeedback = '';
+        $question->generalfeedbackformat = FORMAT_MOODLE;
         $question->correctfeedback = '';
         $question->partiallycorrectfeedback = '';
         $question->incorrectfeedback = '';
index d9db4bb..4e64c2f 100644 (file)
@@ -76,12 +76,11 @@ class qformat_aiken extends qformat_default {
                 }
                 if (preg_match('/^[A-Z][).][ \t]/', $nowline)) {
                     // A choice. Trim off the label and space, then save
-                    $question->answer[] = htmlspecialchars(trim(substr($nowline, 2)), ENT_NOQUOTES);
+                    $question->answer[] = $this->text_field(
+                            htmlspecialchars(trim(substr($nowline, 2)), ENT_NOQUOTES));
                     $question->fraction[] = 0;
-                    $question->feedback[] = '';
-                    continue;
-                }
-                if (preg_match('/^ANSWER:/', $nowline)) {
+                    $question->feedback[] = $this->text_field('');
+                } else if (preg_match('/^ANSWER:/', $nowline)) {
                     // The line that indicates the correct answer. This question is finised.
                     $ans = trim(substr($nowline, strpos($nowline, ':') + 1));
                     $ans = substr($ans, 0, 1);
@@ -97,15 +96,31 @@ class qformat_aiken extends qformat_default {
                     // Must be the first line of a new question, since no recognised prefix.
                     $question->qtype = MULTICHOICE;
                     $question->name = shorten_text(s($nowline), 50);
-                    $question->questiontext = s($nowline);
+                    $question->questiontext = htmlspecialchars(trim($nowline), ENT_NOQUOTES);
+                    $question->questiontextformat = FORMAT_HTML;
+                    $question->generalfeedback = '';
+                    $question->generalfeedbackformat = FORMAT_HTML;
                     $question->single = 1;
-                    $question->feedback[] = '';
+                    $question->answer = array();
+                    $question->fraction = array();
+                    $question->feedback = array();
+                    $question->correctfeedback = $this->text_field('');
+                    $question->partiallycorrectfeedback = $this->text_field('');
+                    $question->incorrectfeedback = $this->text_field('');
                 }
             }
         }
         return $questions;
     }
 
+    protected function text_field($text) {
+        return array(
+            'text' => htmlspecialchars(trim($text), ENT_NOQUOTES),
+            'format' => FORMAT_HTML,
+            'files' => array(),
+        );
+    }
+
     public function readquestion($lines) {
         //this is no longer needed but might still be called by default.php
         return;
diff --git a/question/format/aiken/simpletest/fixtures/questions.aiken.txt b/question/format/aiken/simpletest/fixtures/questions.aiken.txt
new file mode 100644 (file)
index 0000000..b8c24d4
--- /dev/null
@@ -0,0 +1,11 @@
+The Moodle project was started by:
+A) Petr Skoda
+B) Martin Dougiamas
+C) Eloy Lafuente
+D) Tim Hunt
+ANSWER: B
+
+Moodle's abilities include handling user input that includes <html class="cool"> & images:
+A) True
+B) False
+ANSWER: A
index f1a511e..f75a548 100644 (file)
@@ -567,22 +567,25 @@ class rating_manager {
         $params['component']    = $options->component;
         $params['ratingarea'] = $options->ratingarea;
 
-        $sql = "SELECT r.itemid, r.component, r.ratingarea, r.contextid,
-                       $aggregatestr(r.rating) AS aggrrating, COUNT(r.rating) AS numratings,
-                       ur.id, ur.userid, ur.scaleid, ur.rating AS usersrating
+        $sql = "SELECT r.id, r.itemid, r.userid, r.scaleid, r.rating AS usersrating
+                  FROM {rating} r
+                 WHERE r.userid = :userid AND
+                       r.contextid = :contextid AND
+                       r.itemid {$itemidtest} AND
+                       r.component = :component AND
+                       r.ratingarea = :ratingarea
+              ORDER BY r.itemid";
+        $userratings = $DB->get_records_sql($sql, $params);
+
+        $sql = "SELECT r.itemid, $aggregatestr(r.rating) AS aggrrating, COUNT(r.rating) AS numratings
                   FROM {rating} r
-             LEFT JOIN {rating} ur ON ur.contextid = r.contextid AND
-                                      ur.itemid = r.itemid AND
-                                      ur.component = r.component AND
-                                      ur.ratingarea = r.ratingarea AND
-                                      ur.userid = :userid
                  WHERE r.contextid = :contextid AND
                        r.itemid {$itemidtest} AND
                        r.component = :component AND
                        r.ratingarea = :ratingarea
-              GROUP BY r.itemid, r.component, r.ratingarea, r.contextid, ur.id, ur.userid, ur.scaleid
+              GROUP BY r.itemid, r.component, r.ratingarea, r.contextid
               ORDER BY r.itemid";
-        $ratingsrecords = $DB->get_records_sql($sql, $params);
+        $aggregateratings = $DB->get_records_sql($sql, $params);
 
         $ratingoptions = new stdClass;
         $ratingoptions->context = $options->context;
@@ -590,25 +593,37 @@ class rating_manager {
         $ratingoptions->ratingarea = $options->ratingarea;
         $ratingoptions->settings = $this->generate_rating_settings_object($options);
         foreach ($options->items as $item) {
-            if (array_key_exists($item->{$itemidcol}, $ratingsrecords)) {
-                // Note: rec->scaleid = the id of scale at the time the rating was submitted
-                // may be different from the current scale id
-                $rec = $ratingsrecords[$item->{$itemidcol}];
+            $founduserrating = false;
+            foreach($userratings as $userrating) {
+                //look for an existing rating from this user of this item
+                if ($item->{$itemidcol} == $userrating->itemid) {
+                    // Note: rec->scaleid = the id of scale at the time the rating was submitted
+                    // may be different from the current scale id
+                    $ratingoptions->scaleid = $userrating->scaleid;
+                    $ratingoptions->userid = $userrating->userid;
+                    $ratingoptions->id = $userrating->id;
+                    $ratingoptions->rating = min($userrating->usersrating, $ratingoptions->settings->scale->max);
+
+                    $founduserrating = true;
+                    break;
+                }
+            }
+            if (!$founduserrating) {
+                $ratingoptions->scaleid = null;
+                $ratingoptions->userid = null;
+                $ratingoptions->id = null;
+                $ratingoptions->rating =  null;
+            }
+
+            if (array_key_exists($item->{$itemidcol}, $aggregateratings)) {
+                $rec = $aggregateratings[$item->{$itemidcol}];
                 $ratingoptions->itemid = $item->{$itemidcol};
-                $ratingoptions->scaleid = $rec->scaleid;
-                $ratingoptions->userid = $rec->userid;
-                $ratingoptions->id = $rec->id;
                 $ratingoptions->aggregate = min($rec->aggrrating, $ratingoptions->settings->scale->max);
                 $ratingoptions->count = $rec->numratings;
-                $ratingoptions->rating = min($rec->usersrating, $ratingoptions->settings->scale->max);
             } else {
                 $ratingoptions->itemid = $item->{$itemidcol};
-                $ratingoptions->scaleid = null;
-                $ratingoptions->userid = null;
-                $ratingoptions->id = null;
                 $ratingoptions->aggregate = null;
                 $ratingoptions->count = 0;
-                $ratingoptions->rating =  null;
             }
 
             $rating = new rating($ratingoptions);
diff --git a/rating/simpletest/testrating.php b/rating/simpletest/testrating.php
new file mode 100644 (file)
index 0000000..910544c
--- /dev/null
@@ -0,0 +1,225 @@
+<?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 rating/lib.php
+ *
+ * @package    moodlecore
+ * @subpackage rating
+ * @copyright  2011 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+// Include all the needed stuff
+require_once($CFG->dirroot . '/rating/lib.php');
+
+/**
+ * Unit test case for all the rating/lib.php requiring DB mockup & manipulation
+ */
+class rating_db_test extends UnitTestCaseUsingDatabase {
+
+    public static $includecoverage = array(
+        'rating/lib.php'
+    );
+
+    protected $testtables = array(
+            'lib' => array(
+                'rating', 'scale'));
+
+    public function setUp() {
+        parent::setUp();
+
+        $this->switch_to_test_db(); // Switch to test DB for all the execution
+
+        foreach ($this->testtables as $dir => $tables) {
+            $this->create_test_tables($tables, $dir); // Create tables
+        }
+    }
+
+    /**
+     * Test the current get_ratings method main sql
+     */
+    function test_get_ratings_sql() {
+
+        // We load 3 items. Each is rated twice. For simplicity itemid == user id of the item owner
+        $ctxid = SYSCONTEXTID;
+        $this->load_test_data('rating',
+                array('contextid', 'component', 'ratingarea', 'itemid', 'scaleid', 'rating', 'userid', 'timecreated', 'timemodified'), array(
+
+                //user 1's items. Average == 2
+                array(    $ctxid , 'mod_forum',       'post',       1 ,       10 ,       1 ,       2 ,            1 ,              1),
+                array(    $ctxid , 'mod_forum',       'post',       1 ,       10 ,       3 ,       3 ,            1 ,              1),
+
+                //user 2's items. Average == 3
+                array(    $ctxid , 'mod_forum',       'post',       2 ,       10 ,       1 ,       1 ,            1 ,              1),
+                array(    $ctxid , 'mod_forum',       'post',       2 ,       10 ,       5 ,       3 ,            1 ,              1),
+
+                //user 3's items. Average == 4
+                array(    $ctxid , 'mod_forum',       'post',       3 ,       10 ,       3 ,       1 ,            1 ,              1),
+                array(    $ctxid , 'mod_forum',       'post',       3 ,       10 ,       5 ,       2 ,            1 ,              1)
+                ));
+
+        // a post (item) by user 1 (rated above by user 2 and 3 with average = 2)
+        $user1posts = array(
+                (object)array('id' => 1, 'userid' => 1, 'message' => 'hello'));
+        // a post (item) by user 2 (rated above by user 1 and 3 with average = 3)
+        $user2posts = array(
+                (object)array('id' => 2, 'userid' => 2, 'message' => 'world'));
+        // a post (item) by user 3 (rated above by user 1 and 2 with average = 4)
+        $user3posts = array(
+                (object)array('id' => 3, 'userid' => 3, 'message' => 'moodle'));
+
+        // Prepare the default options
+        $defaultoptions = array (
+                'context'    => get_context_instance(CONTEXT_SYSTEM),
+                'component'  => 'mod_forum',
+                'ratingarea' => 'post',
+                'scaleid'    => 10,
+                'aggregate'  => RATING_AGGREGATE_AVERAGE);
+
+        $rm = new rating_manager();
+
+        // STEP 1: Retreive ratings using the current user
+
+        // Get results for user 1's item (expected average 1 + 3 / 2 = 2)
+        $toptions = (object)array_merge($defaultoptions, array('items' => $user1posts));
+        $result = $rm->get_ratings($toptions);
+        $this->assertEqual(count($result), count($user1posts));
+        $this->assertEqual($result[0]->id, $user1posts[0]->id);
+        $this->assertEqual($result[0]->userid, $user1posts[0]->userid);
+        $this->assertEqual($result[0]->message, $user1posts[0]->message);
+        $this->assertEqual($result[0]->rating->count, 2);
+        $this->assertEqual($result[0]->rating->aggregate, 2);
+        // Note that $result[0]->rating->rating is somewhat random
+        // We didn't supply a user ID so $USER was used which will vary depending on who runs the tests
+
+        // Get results for items of user 2 (expected average 1 + 5 / 2 = 3)
+        $toptions = (object)array_merge($defaultoptions, array('items' => $user2posts));
+        $result = $rm->get_ratings($toptions);
+        $this->assertEqual(count($result), count($user2posts));
+        $this->assertEqual($result[0]->id, $user2posts[0]->id);
+        $this->assertEqual($result[0]->userid, $user2posts[0]->userid);
+        $this->assertEqual($result[0]->message, $user2posts[0]->message);
+        $this->assertEqual($result[0]->rating->count, 2);
+        $this->assertEqual($result[0]->rating->aggregate, 3);
+        // Note that $result[0]->rating->rating is somewhat random
+        // We didn't supply a user ID so $USER was used which will vary depending on who runs the tests
+
+        // Get results for items of user 3 (expected average 3 + 5 / 2 = 4)
+        $toptions = (object)array_merge($defaultoptions, array('items' => $user3posts));
+        $result = $rm->get_ratings($toptions);
+        $this->assertEqual(count($result), count($user3posts));
+        $this->assertEqual($result[0]->id, $user3posts[0]->id);
+        $this->assertEqual($result[0]->userid, $user3posts[0]->userid);
+        $this->assertEqual($result[0]->message, $user3posts[0]->message);
+        $this->assertEqual($result[0]->rating->count, 2);
+        $this->assertEqual($result[0]->rating->aggregate, 4);
+        // Note that $result[0]->rating->rating is somewhat random
+        // We didn't supply a user ID so $USER was used which will vary depending on who runs the tests
+
+        // Get results for items of user 1 & 2 together (expected averages are 2 and 3, as tested above)
+        $posts = array_merge($user1posts, $user2posts);
+        $toptions = (object)array_merge($defaultoptions, array('items' => $posts));
+        $result = $rm->get_ratings($toptions);
+        $this->assertEqual(count($result), count($posts));
+        $this->assertEqual($result[0]->id, $posts[0]->id);
+        $this->assertEqual($result[0]->userid, $posts[0]->userid);
+        $this->assertEqual($result[0]->message, $posts[0]->message);
+        $this->assertEqual($result[0]->rating->count, 2);
+        $this->assertEqual($result[0]->rating->aggregate, 2);
+        // Note that $result[0]->rating->rating is somewhat random
+        // We didn't supply a user ID so $USER was used which will vary depending on who runs the tests
+
+        $this->assertEqual($result[1]->id, $posts[1]->id);
+        $this->assertEqual($result[1]->userid, $posts[1]->userid);
+        $this->assertEqual($result[1]->message, $posts[1]->message);
+        $this->assertEqual($result[1]->rating->count, 2);
+        $this->assertEqual($result[1]->rating->aggregate, 3);
+        // Note that $result[0]->rating->rating is somewhat random
+        // We didn't supply a user ID so $USER was used which will vary depending on who runs the tests
+
+        // STEP 2: Retrieve ratings by a specified user
+        //         We still expect complete aggregations and counts
+
+        // Get results for items of user 1 rated by user 2 (avg 2, rating 1)
+        $toptions = (object)array_merge($defaultoptions, array('items' => $user1posts, 'userid' => 2));
+        $result = $rm->get_ratings($toptions);
+        $this->assertEqual(count($result), count($user1posts));
+        $this->assertEqual($result[0]->id, $user1posts[0]->id);
+        $this->assertEqual($result[0]->userid, $user1posts[0]->userid);
+        $this->assertEqual($result[0]->message, $user1posts[0]->message);
+        $this->assertEqual($result[0]->rating->count, 2);
+        $this->assertEqual($result[0]->rating->aggregate, 2);
+        $this->assertEqual($result[0]->rating->rating, 1); //user 2 rated user 1 "1"
+        $this->assertEqual($result[0]->rating->userid, $toptions->userid); // Must be the passed userid
+
+        // Get results for items of user 1 rated by user 3
+        $toptions = (object)array_merge($defaultoptions, array('items' => $user1posts, 'userid' => 3));
+        $result = $rm->get_ratings($toptions);
+        $this->assertEqual(count($result), count($user1posts));
+        $this->assertEqual($result[0]->id, $user1posts[0]->id);
+        $this->assertEqual($result[0]->userid, $user1posts[0]->userid);
+        $this->assertEqual($result[0]->message, $user1posts[0]->message);
+        $this->assertEqual($result[0]->rating->count, 2);
+        $this->assertEqual($result[0]->rating->aggregate, 2);
+        $this->assertEqual($result[0]->rating->rating, 3); //user 3 rated user 1 "3"
+        $this->assertEqual($result[0]->rating->userid, $toptions->userid); // Must be the passed userid
+
+        // Get results for items of user 1 & 2 together rated by user 3
+        $posts = array_merge($user1posts, $user2posts);
+        $toptions = (object)array_merge($defaultoptions, array('items' => $posts, 'userid' => 3));
+        $result = $rm->get_ratings($toptions);
+        $this->assertEqual(count($result), count($posts));
+        $this->assertEqual($result[0]->id, $posts[0]->id);
+        $this->assertEqual($result[0]->userid, $posts[0]->userid);
+        $this->assertEqual($result[0]->message, $posts[0]->message);
+        $this->assertEqual($result[0]->rating->count, 2);
+        $this->assertEqual($result[0]->rating->aggregate, 2);
+        $this->assertEqual($result[0]->rating->rating, 3); //user 3 rated user 1 "3"
+        $this->assertEqual($result[0]->rating->userid, $toptions->userid); // Must be the passed userid
+
+        $this->assertEqual($result[1]->id, $posts[1]->id);
+        $this->assertEqual($result[1]->userid, $posts[1]->userid);
+        $this->assertEqual($result[1]->message, $posts[1]->message);
+        $this->assertEqual($result[1]->rating->count, 2);
+        $this->assertEqual($result[1]->rating->aggregate, 3);
+        $this->assertEqual($result[0]->rating->rating, 3); //user 3 rated user 2 "5"
+        $this->assertEqual($result[1]->rating->userid, $toptions->userid); // Must be the passed userid
+
+        // STEP 3: Some special cases
+
+        // Get results for user 1's items (expected average 1 + 3 / 2 = 2)
+        // supplying a non-existent user id so no rating from that user should be found
+        $toptions = (object)array_merge($defaultoptions, array('items' => $user1posts));
+        $toptions->userid = 123456; //non-existent user
+        $result = $rm->get_ratings($toptions);
+        $this->assertNull($result[0]->rating->userid);
+        $this->assertNull($result[0]->rating->rating);
+        $this->assertEqual($result[0]->rating->aggregate, 2);//should still get the aggregate
+
+        // Get results for items of user 2 (expected average 1 + 5 / 2 = 3)
+        // Supplying the user id of the user who owns the items so no rating should be found
+        $toptions = (object)array_merge($defaultoptions, array('items' => $user2posts));
+        $toptions->userid = 2; //user 2 viewing the ratings of their own item
+        $result = $rm->get_ratings($toptions);
+        //these should be null as the user is viewing their own item and thus cannot rate
+        $this->assertNull($result[0]->rating->userid);
+        $this->assertNull($result[0]->rating->rating);
+        $this->assertEqual($result[0]->rating->aggregate, 3);//should still get the aggregate
+    }
+}
index ab374d6..4f36475 100644 (file)
@@ -99,10 +99,6 @@ class repository_local extends repository {
                         continue;
                     }
                     $params = $child->get_params();
-                    $subdir_children = $child->get_children();
-                    //if (empty($subdir_children)) {
-                        //continue;
-                    //}
                     $encodedpath = base64_encode(serialize($params));
                     // hide user_private area from local plugin, user should
                     // use private file plugin to access private files