Merge branch 'master_MDL-27036' of git://github.com/danmarsden/moodle
authorEloy Lafuente (stronk7) <stronk7@moodle.org>
Wed, 9 Nov 2011 01:03:25 +0000 (02:03 +0100)
committerEloy Lafuente (stronk7) <stronk7@moodle.org>
Wed, 9 Nov 2011 01:03:25 +0000 (02:03 +0100)
28 files changed:
backup/restore.php
backup/util/ui/renderer.php
backup/util/ui/restore_ui.class.php
backup/util/ui/restore_ui_stage.class.php
backup/util/ui/yui/confirmcancel/confirmcancel.js
blocks/html/block_html.php
course/lib.php
course/report/log/lib.php
lang/en/backup.php
lib/filestorage/file_types.mm
lib/minify/lib/Minify/CSS/Compressor.php
lib/minify/readme_moodle.txt
lib/modinfolib.php
lib/webdavlib.php
mod/assignment/lib.php
mod/assignment/type/upload/assignment.class.php
mod/lesson/pagetypes/truefalse.php
mod/quiz/locallib.php
mod/scorm/lang/en/scorm.php
mod/scorm/report/interactions/lang/en/scormreport_interactions.php [new file with mode: 0644]
mod/scorm/report/interactions/report.php [new file with mode: 0644]
mod/scorm/report/interactions/responsessettings_form.php [new file with mode: 0644]
mod/scorm/report/interactions/version.php [new file with mode: 0644]
mod/scorm/report/reportlib.php
question/format/xml/format.php
question/format/xml/simpletest/testxmlformat.php
question/preview.php
repository/webdav/lib.php

index ed72ab8..04ed41a 100644 (file)
@@ -42,7 +42,12 @@ if ($stage & restore_ui::STAGE_CONFIRM + restore_ui::STAGE_DESTINATION) {
 $outcome = $restore->process();
 if (!$restore->is_independent()) {
     if ($restore->get_stage() == restore_ui::STAGE_PROCESS && !$restore->requires_substage()) {
-        $restore->execute();
+        try {
+            $restore->execute();
+        } catch(Exception $e) {
+            $restore->cleanup();
+            throw new moodle_exception((string)$e);
+        }
     } else {
         $restore->save_controller();
     }
index d62a243..4ca5414 100644 (file)
@@ -211,7 +211,7 @@ class core_backup_renderer extends plugin_renderer_base {
      * @return string
      */
     public function course_selector(moodle_url $nextstageurl, $wholecourse = true, restore_category_search $categories = null, restore_course_search $courses=null, $currentcourse = null) {
-        global $CFG;
+        global $CFG, $PAGE;
         require_once($CFG->dirroot.'/course/lib.php');
 
         $nextstageurl->param('sesskey', sesskey());
@@ -232,9 +232,15 @@ class core_backup_renderer extends plugin_renderer_base {
             $html .= $this->output->heading(get_string('restoretonewcourse', 'backup'), 2, array('class'=>'header'));
             $html .= $this->backup_detail_input(get_string('restoretonewcourse', 'backup'), 'radio', 'target', backup::TARGET_NEW_COURSE, array('checked'=>'checked'));
             $html .= $this->backup_detail_pair(get_string('selectacategory', 'backup'), $this->render($categories));
-            $html .= $this->backup_detail_pair('', html_writer::empty_tag('input', array('type'=>'submit', 'value'=>get_string('continue'))));
+            $html .= $this->backup_detail_pair('', html_writer::empty_tag('input', array('type'=>'submit', 'value'=>get_string('continue'), 'class'=>'newcoursecontinue')));
             $html .= html_writer::end_tag('div');
             $html .= html_writer::end_tag('form');
+            $config = new stdClass;
+            $config->title = get_string('confirmnewcoursecontinue', 'backup');
+            $config->question = get_string('confirmnewcoursecontinuequestion', 'backup');
+            $config->yesLabel = get_string('continue');
+            $config->noLabel = get_string('cancel');
+            $PAGE->requires->yui_module('moodle-backup-confirmcancel', 'M.core_backup.watch_newcoursecontinue_buttons', array($config));
         }
 
         if ($wholecourse && !empty($currentcourse)) {
index 220cc23..b932508 100644 (file)
@@ -146,6 +146,39 @@ class restore_ui extends base_ui {
         $this->stage = new restore_ui_stage_complete($this, $this->stage->get_params(), $this->controller->get_results());
         return true;
     }
+
+    /**
+     * Delete course which is created by restore process
+     */
+    public function cleanup() {
+        $courseid = $this->controller->get_courseid();
+        if ($this->is_temporary_course_created($courseid)) {
+            delete_course($courseid, false);
+        }
+    }
+
+    /**
+     * Checks if the course is not restored fully and current controller has created it.
+     * @param int $courseid id of the course which needs to be checked
+     * @return bool
+     */
+    protected function is_temporary_course_created($courseid) {
+        global $DB;
+        //Check if current controller instance has created new course.
+        if ($this->controller->get_target() == backup::TARGET_NEW_COURSE) {
+            $results = $DB->record_exists_sql("SELECT bc.itemid
+                                               FROM {backup_controllers} bc, {course} c
+                                               WHERE bc.operation = 'restore'
+                                                 AND bc.type = 'course'
+                                                 AND bc.itemid = c.id
+                                                 AND bc.itemid = ?",
+                                               array($courseid)
+                                             );
+            return $results;
+        }
+        return false;
+    }
+
     /**
      * Returns true if enforce_dependencies changed any settings
      * @return bool
@@ -191,15 +224,12 @@ class restore_ui extends base_ui {
     /**
      * Cancels the current restore and redirects the user back to the relevant place
      */
-    public function cancel_restore() {
-        global $PAGE;
-        // Determine the approriate URL to redirect the user to
-        if ($PAGE->context->contextlevel == CONTEXT_MODULE && $PAGE->cm !== null) {
-            $relevanturl = new moodle_url('/mod/'.$PAGE->cm->modname.'/view.php', array('id'=>$PAGE->cm->id));
-        } else {
-            $relevanturl = new moodle_url('/course/view.php', array('id'=>$PAGE->course->id));
+    public function cancel_process() {
+        //Delete temporary restore course if exists.
+        if ($this->controller->get_target() == backup::TARGET_NEW_COURSE) {
+            $this->cleanup();
         }
-        redirect($relevanturl);
+        parent::cancel_process();
     }
     /**
      * Gets an array of progress bar items that can be displayed through the restore renderer.
index cd19b7d..478fe7a 100644 (file)
@@ -333,7 +333,7 @@ class restore_ui_stage_settings extends restore_ui_stage {
         $form = $this->initialise_stage_form();
 
         if ($form->is_cancelled()) {
-            $this->ui->cancel_restore();
+            $this->ui->cancel_process();
         }
 
         $data = $form->get_data();
index 267b853..05395e4 100644 (file)
@@ -27,4 +27,24 @@ M.core_backup.watch_cancel_buttons = function(config) {
     });
 }
 
+M.core_backup.watch_newcoursecontinue_buttons = function(config) {
+    Y.all('.newcoursecontinue').each(function(){
+        this._confirmationListener = this._confirmationListener || this.on('click', function(e){
+            // Prevent the default event (sumbit) from firing
+            e.preventDefault();
+            // Create the confirm box
+            var confirm = new M.core.confirm(config);
+            // If the user clicks yes
+            confirm.on('complete-yes', function(e){
+                // Detach the listener for the confirm box so it doesn't fire again.
+                this._confirmationListener.detach();
+                // Simulate the original cancel button click
+                this.simulate('click');
+            }, this);
+            // Show the confirm box
+            confirm.show();
+        }, this);
+    });
+}
+
 }, '@VERSION@', {'requires':['base','node','node-event-simulate','moodle-enrol-notification']});
\ No newline at end of file
index bead128..f030401 100644 (file)
@@ -121,4 +121,14 @@ class block_html extends block_base {
 
         return true;
     }
+
+    /**
+     * The block should only be dockable when the title of the block is not empty
+     * and when parent allows docking.
+     *
+     * @return bool
+     */
+    public function instance_can_be_docked() {
+        return (!empty($this->config->title) && parent::instance_can_be_docked());
+    }
 }
index 79ccf91..8fc57f2 100644 (file)
@@ -1149,6 +1149,9 @@ function get_array_of_activities($courseid) {
                                if (!empty($info->extraclasses)) {
                                    $mod[$seq]->extraclasses = $info->extraclasses;
                                }
+                               if (!empty($info->iconurl)) {
+                                   $mod[$seq]->iconurl = $info->iconurl;
+                               }
                                if (!empty($info->onclick)) {
                                    $mod[$seq]->onclick = $info->onclick;
                                }
@@ -1186,7 +1189,7 @@ function get_array_of_activities($courseid) {
                    // Minimise the database size by unsetting default options when they are
                    // 'empty'. This list corresponds to code in the cm_info constructor.
                    foreach (array('idnumber', 'groupmode', 'groupingid', 'groupmembersonly',
-                           'indent', 'completion', 'extra', 'extraclasses', 'onclick', 'content',
+                           'indent', 'completion', 'extra', 'extraclasses', 'iconurl', 'onclick', 'content',
                            'icon', 'iconcomponent', 'customdata', 'showavailability', 'availablefrom',
                            'availableuntil', 'conditionscompletion', 'conditionsgrade',
                            'completionview', 'completionexpected', 'score', 'showdescription')
index eca683b..06ded6a 100644 (file)
@@ -350,7 +350,12 @@ function print_log_selector_form($course, $selecteduser=0, $selecteddate='today'
     // Get all the possible users
     $users = array();
 
-    $courseusers = get_enrolled_users($context, '', $selectedgroup, 'u.id, u.firstname, u.lastname, u.idnumber', 'lastname ASC, firstname ASC');
+    // Define limitfrom and limitnum for queries below
+    // If $showusers is enabled... don't apply limitfrom and limitnum
+    $limitfrom = empty($showusers) ? 0 : '';
+    $limitnum  = empty($showusers) ? COURSE_MAX_USERS_PER_DROPDOWN + 1 : '';
+
+    $courseusers = get_enrolled_users($context, '', $selectedgroup, 'u.id, u.firstname, u.lastname', 'lastname ASC, firstname ASC', $limitfrom, $limitnum);
 
     if (count($courseusers) < COURSE_MAX_USERS_PER_DROPDOWN && !$showusers) {
         $showusers = 1;
@@ -552,4 +557,4 @@ function log_page_type_list($pagetype, $parentcontext, $currentcontext) {
         //course-report-log-live not included as theres no blocks on the live log page
     );
     return $array;
-}
\ No newline at end of file
+}
index 595c8a1..25c5718 100644 (file)
@@ -92,6 +92,8 @@ $string['confirmcancelquestion'] = 'Are you sure you wish to cancel?
 Any information you have entered will be lost.';
 $string['confirmcancelyes'] = 'Cancel';
 $string['confirmcancelno'] = 'Stay';
+$string['confirmnewcoursecontinue'] = 'New course warning';
+$string['confirmnewcoursecontinuequestion'] = 'A temporary (hidden) course will be created by the course restoration process. To abort restoration click cancel. Do not close the browser while restoring.';
 $string['coursecategory'] = 'Category the course will be restored into';
 $string['courseid'] = 'Original ID';
 $string['coursesettings'] = 'Course settings';
index 64a12bb..71ff4fb 100644 (file)
@@ -36,6 +36,9 @@
 <node ID="_Freemind_Link_810187762" TEXT=".wmv"/>
 <node ID="_Freemind_Link_763870397" TEXT=".asf"/>
 <node TEXT=".ogv"/>
+<node TEXT=".f4v"/>
+<node TEXT=".m4v"/>
+<node TEXT=".webm"/>
 </node>
 <node ID="_Freemind_Link_1019644700" TEXT="non_web_video">
 <node ID="_Freemind_Link_190589975" TEXT=".avi"/>
index d483b2f..dad700a 100644 (file)
@@ -236,15 +236,16 @@ class Minify_CSS_Compressor {
      */
     protected function _fontFamilyCB($m)
     {
-        $m[1] = preg_replace('/
-                \\s*
-                (
-                    "[^"]+"      # 1 = family in double qutoes
-                    |\'[^\']+\'  # or 1 = family in single quotes
-                    |[\\w\\-]+   # or 1 = unquoted family
-                )
-                \\s*
-            /x', '$1', $m[1]);
-        return 'font-family:' . $m[1] . $m[2];
+        // Issue 210: must not eliminate WS between words in unquoted families
+        $pieces = preg_split('/(\'[^\']+\'|"[^"]+")/', $m[1], null, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
+        $out = 'font-family:';
+        while (null !== ($piece = array_shift($pieces))) {
+            if ($piece[0] !== '"' && $piece[0] !== "'") {
+                $piece = preg_replace('/\\s+/', ' ', $piece);
+                $piece = preg_replace('/\\s?,\\s?/', ',', $piece);
+            }
+            $out .= $piece;
+        }
+        return $out . $m[2];
     }
 }
index bd798f2..bc14db5 100644 (file)
@@ -15,3 +15,6 @@ Changes:
  * Removed .htaccess - Not needed
  * Changed config.php - Changed settings to Moodle specific settings incase this
    ever gets accidentally used.
+ * Updated lib/Minify/CSS/Compressor.php - Applied an upstream fix for MDL-29864
+   to allow usage of unquoted font-familes with spaces in CSS.
+   http://code.google.com/p/minify/issues/detail?id=210
index bdf82ff..20ac75c 100644 (file)
@@ -578,6 +578,11 @@ class cm_info extends stdClass  {
      */
     private $extraclasses;
 
+    /**
+     * @var moodle_url full external url pointing to icon image for activity
+     */
+    private $iconurl;
+
     /**
      * @var string
      */
@@ -650,7 +655,7 @@ class cm_info extends stdClass  {
 
     /**
      * Note: Will collect view data, if not already obtained.
-                * @return string Extra HTML code to display after link
+     * @return string Extra HTML code to display after link
      */
     public function get_after_link() {
         $this->obtain_view_data();
@@ -675,7 +680,12 @@ class cm_info extends stdClass  {
         if (!$output) {
             $output = $OUTPUT;
         }
-        if (!empty($this->icon)) {
+        // Support modules setting their own, external, icon image
+        if (!empty($this->iconurl)) {
+            $icon = $this->iconurl;
+
+        // Fallback to normal local icon + component procesing
+        } else if (!empty($this->icon)) {
             if (substr($this->icon, 0, 4) === 'mod/') {
                 list($modname, $iconname) = explode('/', substr($this->icon, 4), 2);
                 $icon = $output->pix_url($iconname, $modname);
@@ -729,6 +739,18 @@ class cm_info extends stdClass  {
         $this->extraclasses = $extraclasses;
     }
 
+    /**
+     * Sets the external full url that points to the icon being used
+     * by the activity. Useful for external-tool modules (lti...)
+     * If set, takes precedence over $icon and $iconcomponent
+     *
+     * @param moodle_url $iconurl full external url pointing to icon image for activity
+     * @return void
+     */
+    public function set_icon_url(moodle_url $iconurl) {
+        $this->iconurl = $iconurl;
+    }
+
     /**
      * Sets value of on-click attribute for JavaScript.
      * Note: May not be called from _cm_info_view (only _cm_info_dynamic).
@@ -856,6 +878,7 @@ class cm_info extends stdClass  {
         $this->indent           = isset($mod->indent) ? $mod->indent : 0;
         $this->extra            = isset($mod->extra) ? $mod->extra : '';
         $this->extraclasses     = isset($mod->extraclasses) ? $mod->extraclasses : '';
+        $this->iconurl          = isset($mod->iconurl) ? $mod->iconurl : '';
         $this->onclick          = isset($mod->onclick) ? $mod->onclick : '';
         $this->content          = isset($mod->content) ? $mod->content : '';
         $this->icon             = isset($mod->icon) ? $mod->icon : '';
@@ -1199,9 +1222,16 @@ class cached_cm_info {
      */
     public $extraclasses;
 
+    /**
+     * External URL image to be used by activity as icon, useful for some external-tool modules
+     * like lti. If set, takes precedence over $icon and $iconcomponent
+     * @var $moodle_url
+     */
+    public $iconurl;
+
     /**
      * Content of onclick JavaScript; escaped HTML to be inserted as attribute value
      * @var string
      */
     public $onclick;
-}
\ No newline at end of file
+}
index 4eddc7d..70bff78 100644 (file)
@@ -797,7 +797,7 @@ EOD;
                 if (strcmp($response['status']['status-code'],'207') == 0 ) {
                     // ok so far
                     // next there should be a Content-Type: text/xml; charset="utf-8" header line
-                    if (preg_match('#text/xml;\s?charset=[\'\"]?utf-8[\'\"]?#i', $response['header']['Content-Type'])) {
+                    if (preg_match('#(application|text)/xml;\s?charset=[\'\"]?utf-8[\'\"]?#i', $response['header']['Content-Type'])) {
                         // ok let's get the content of the xml stuff
                         $this->_parser = xml_parser_create_ns('UTF-8');
                         // forget old data...
@@ -1401,22 +1401,48 @@ EOD;
             // check for a specified content-length
         case preg_match('/Content\\-Length:\\s+([0-9]*)\\r\\n/',$header,$matches):
             $this->_error_log('Getting data using Content-Length '. $matches[1]);
+
             // check if we the content data size is small enough to get it as one block
             if ($matches[1] <= $max_chunk_size ) {
                 // only read something if Content-Length is bigger than 0
                 if ($matches[1] > 0 ) {
                     $buffer = fread($this->sock, $matches[1]);
+                    $loadsize = strlen($buffer);
+                    //did we realy get the full length?
+                    if ($loadsize < $matches[1]) {
+                        $max_chunk_size = $loadsize;
+                        do {
+                            $mod = $max_chunk_size % ($matches[1] - strlen($buffer));
+                            $chunk_size = ($mod == $max_chunk_size ? $max_chunk_size : $matches[1] - strlen($buffer));
+                            $buffer .= fread($this->sock, $chunk_size);
+                            $this->_error_log('mod: ' . $mod . ' chunk: ' . $chunk_size . ' total: ' . strlen($buffer));
+                        } while ($mod == $max_chunk_size);
+                        break;
+                    } else {
+                        break;
+                    }
                 } else {
                     $buffer = '';
+                    break;
                 }
-            } else {
-                // data is to big to handle it as one. Get it chunk per chunk...
-                do {
-                    $mod = $max_chunk_size % ($matches[1] - strlen($buffer));
-                    $chunk_size = ($mod == $max_chunk_size ? $max_chunk_size : $matches[1] - strlen($buffer));
-                    $buffer .= fread($this->sock, $chunk_size);
-                    $this->_error_log('mod: ' . $mod . ' chunk: ' . $chunk_size . ' total: ' . strlen($buffer));
-                } while ($mod == $max_chunk_size);
+            }
+
+            // data is to big to handle it as one. Get it chunk per chunk...
+            //trying to get the full length of max_chunk_size
+            $buffer = fread($this->sock, $max_chunk_size);
+            $loadsize = strlen($buffer);
+            if ($loadsize < $max_chunk_size) {
+                $max_chunk_size = $loadsize;
+            }
+            do {
+                $mod = $max_chunk_size % ($matches[1] - strlen($buffer));
+                $chunk_size = ($mod == $max_chunk_size ? $max_chunk_size : $matches[1] - strlen($buffer));
+                $buffer .= fread($this->sock, $chunk_size);
+                $this->_error_log('mod: ' . $mod . ' chunk: ' . $chunk_size . ' total: ' . strlen($buffer));
+            } while ($mod == $max_chunk_size);
+            $loadsize = strlen($buffer);
+            if ($loadsize < $matches[1]) {
+                $buffer .= fread($this->sock, $matches[1] - $loadsize);
             }
             break;
 
index 727b64e..99adc60 100644 (file)
@@ -1764,7 +1764,7 @@ class assignment_base {
                 $info->username = fullname($user, true);
                 $info->assignment = format_string($this->assignment->name,true);
                 $info->url = $CFG->wwwroot.'/mod/assignment/submissions.php?id='.$this->cm->id;
-                $info->timeupdated = strftime('%c',$submission->timemodified);
+                $info->timeupdated = userdate($submission->timemodified, '%c', $teacher->timezone);
 
                 $postsubject = $strsubmitted.': '.$info->username.' -> '.$this->assignment->name;
                 $posttext = $this->email_teachers_text($info);
index c1d4ffd..6e24785 100644 (file)
@@ -918,9 +918,14 @@ class assignment_upload extends assignment_base {
     }
 
     function can_unfinalize($submission) {
+        if(is_bool($submission)) {
+            return false;
+        }
+
         if (!$this->drafts_tracked()) {
             return false;
         }
+
         if (has_capability('mod/assignment:grade', $this->context)
           and $this->isopen()
           and $this->is_finalized($submission)) {
@@ -932,6 +937,11 @@ class assignment_upload extends assignment_base {
 
     function can_finalize($submission) {
         global $USER;
+
+        if(is_bool($submission)) {
+            return false;
+        }
+
         if (!$this->drafts_tracked()) {
             return false;
         }
index db9a15f..7ce13ec 100644 (file)
@@ -95,7 +95,7 @@ class lesson_page_type_truefalse extends lesson_page {
         }
         $result->newpageid = $answer->jumpto;
         $result->response  = format_text($answer->response, $answer->responseformat, $formattextdefoptions);
-        $result->studentanswer = $result->userresponse = $result->response;
+        $result->studentanswer = $result->userresponse = $answer->answer;
         return $result;
     }
 
index 98ddd21..7b71818 100644 (file)
@@ -671,7 +671,12 @@ function quiz_update_all_final_grades($quiz) {
 
             WHERE
                 ABS(newgrades.newgrade - qg.grade) > 0.000005 OR
-                (newgrades.newgrade IS NULL) <> (qg.grade IS NULL)",
+                ((newgrades.newgrade IS NULL OR qg.grade IS NULL) AND NOT
+                          (newgrades.newgrade IS NULL AND qg.grade IS NULL))",
+                // The mess on the previous line is detecting where the value is
+                // NULL in one column, and NOT NULL in the other, but SQL does
+                // not have an XOR operator, and MS SQL server can't cope with
+                // (newgrades.newgrade IS NULL) <> (qg.grade IS NULL).
             $param);
 
     $timenow = time();
index cce4708..7566a2e 100644 (file)
@@ -43,7 +43,7 @@ $string['areacontent'] = 'Content files';
 $string['areapackage'] = 'Package file';
 $string['asset'] = 'Asset';
 $string['assetlaunched'] = 'Asset - Viewed';
-$string['attempt'] = 'attempt';
+$string['attempt'] = 'Attempt';
 $string['attempts'] = 'Attempts';
 $string['attemptsx'] = '{$a} attempts';
 $string['attempt1'] = '1 attempt';
diff --git a/mod/scorm/report/interactions/lang/en/scormreport_interactions.php b/mod/scorm/report/interactions/lang/en/scormreport_interactions.php
new file mode 100644 (file)
index 0000000..be78f52
--- /dev/null
@@ -0,0 +1,35 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Strings for component 'scorm_interactions' report plugin
+ *
+ * @package   scormreport
+ * @subpackage interactions
+ * @author    Dan Marsden and Ankit Kumar Agarwal
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+$string['pluginname'] = 'Interactions Report';
+$string['questionx'] = 'Question {$a}';
+$string['responsex'] = 'Response {$a}';
+$string['rightanswerx'] = 'Right answer {$a}';
+$string['summaryofquestiontext'] = 'Summary of question';
+$string['summaryofresponse'] = 'Summary of responses';
+$string['summaryofrightanswer'] = 'Summary of right answer';
+
diff --git a/mod/scorm/report/interactions/report.php b/mod/scorm/report/interactions/report.php
new file mode 100644 (file)
index 0000000..219db4c
--- /dev/null
@@ -0,0 +1,599 @@
+<?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/>.
+/**
+ * Core Report class of basic reporting plugin
+ * @package   scormreport
+ * @subpackage interactions
+ * @author    Dan Marsden and Ankit Kumar Agarwal
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+require_once($CFG->dirroot.'/mod/scorm/report/interactions/responsessettings_form.php');
+
+class scorm_interactions_report extends scorm_default_report {
+    /**
+     * displays the full report
+     * @param stdClass $scorm full SCORM object
+     * @param stdClass $cm - full course_module object
+     * @param stdClass $course - full course object
+     * @param string $download - type of download being requested
+     */
+    function display($scorm, $cm, $course, $download) {
+        global $CFG, $DB, $OUTPUT, $PAGE;
+        $contextmodule = get_context_instance(CONTEXT_MODULE, $cm->id);
+        $action = optional_param('action', '', PARAM_ALPHA);
+        $attemptids = optional_param('attemptid', array(), PARAM_RAW);
+
+        if ($action == 'delete' && has_capability('mod/scorm:deleteresponses', $contextmodule) && confirm_sesskey()) {
+            if (scorm_delete_responses($attemptids, $scorm)) { //delete responses.
+                add_to_log($course->id, 'scorm', 'delete attempts', 'report.php?id=' . $cm->id, implode(",", $attemptids), $cm->id);
+                echo $OUTPUT->notification(get_string('scormresponsedeleted', 'scorm'), 'notifysuccess');
+            }
+        }
+
+        // detailed report
+        $mform = new mod_scorm_report_interactions_settings($PAGE->url, compact('currentgroup'));
+        if ($fromform = $mform->get_data()) {
+            $pagesize = $fromform->pagesize;
+            $includeqtext = $fromform->qtext;
+            $includeresp = $fromform->resp;
+            $includeright = $fromform->right;
+            $attemptsmode = !empty($fromform->attemptsmode) ? $fromform->attemptsmode : SCORM_REPORT_ATTEMPTS_ALL_STUDENTS;
+            set_user_preference('scorm_report_pagesize', $pagesize);
+            set_user_preference('scorm_report_interactions_qtext', $includeqtext);
+            set_user_preference('scorm_report_interactions_resp', $includeresp);
+            set_user_preference('scorm_report_interactions_right', $includeright);
+        } else {
+            $pagesize = get_user_preferences('scorm_report_pagesize', 0);
+            $attemptsmode = optional_param('attemptsmode', SCORM_REPORT_ATTEMPTS_STUDENTS_WITH, PARAM_INT);
+            $includeqtext = get_user_preferences('scorm_report_interactions_qtext', 0);
+            $includeresp = get_user_preferences('scorm_report_interactions_resp', 1);
+            $includeright = get_user_preferences('scorm_report_interactions_right', 0);
+        }
+        if ($pagesize < 1) {
+            $pagesize = SCORM_REPORT_DEFAULT_PAGE_SIZE;
+        }
+
+        // select group menu
+        $displayoptions = array();
+        $displayoptions['attemptsmode'] = $attemptsmode;
+        $displayoptions['qtext'] = $includeqtext;
+        $displayoptions['resp'] = $includeresp;
+        $displayoptions['right'] = $includeright;
+
+        $mform->set_data($displayoptions + array('pagesize' => $pagesize));
+        if ($groupmode = groups_get_activity_groupmode($cm)) {   // Groups are being used
+            if (!$download) {
+                groups_print_activity_menu($cm, new moodle_url($PAGE->url, $displayoptions));
+            }
+        }
+        $formattextoptions = array('context' => get_context_instance(CONTEXT_COURSE, $course->id));
+
+        // We only want to show the checkbox to delete attempts
+        // if the user has permissions and if the report mode is showing attempts.
+        $candelete = has_capability('mod/scorm:deleteresponses', $contextmodule)
+                && ($attemptsmode != SCORM_REPORT_ATTEMPTS_STUDENTS_WITH_NO);
+        // select the students
+        $nostudents = false;
+
+        if (empty($currentgroup)) {
+            // all users who can attempt scoes
+            if (!$students = get_users_by_capability($contextmodule, 'mod/scorm:savetrack', '', '', '', '', '', '', false)) {
+                echo $OUTPUT->notification(get_string('nostudentsyet'));
+                $nostudents = true;
+                $allowedlist = '';
+            } else {
+                $allowedlist = array_keys($students);
+            }
+        } else {
+            // all users who can attempt scoes and who are in the currently selected group
+            if (!$groupstudents = get_users_by_capability($contextmodule, 'mod/scorm:savetrack', '', '', '', '', $currentgroup, '', false)) {
+                echo $OUTPUT->notification(get_string('nostudentsingroup'));
+                $nostudents = true;
+                $groupstudents = array();
+            }
+            $allowedlist = ($groupstudents);
+        }
+        if ( !$nostudents ) {
+            // Now check if asked download of data
+            if ($download) {
+                $filename = clean_filename("$course->shortname ".format_string($scorm->name, true,$formattextoptions));
+            }
+
+            // Define table columns
+            $columns = array();
+            $headers = array();
+            if (!$download && $candelete) {
+                $columns[] = 'checkbox';
+                $headers[] = null;
+            }
+            if (!$download && $CFG->grade_report_showuserimage) {
+                $columns[] = 'picture';
+                $headers[] = '';
+            }
+            $columns[] = 'fullname';
+            $headers[] = get_string('name');
+            if ($CFG->grade_report_showuseridnumber) {
+                $columns[] = 'idnumber';
+                $headers[] = get_string('idnumber');
+            }
+            $columns[] = 'attempt';
+            $headers[] = get_string('attempt', 'scorm');
+            $columns[] = 'start';
+            $headers[] = get_string('started', 'scorm');
+            $columns[] = 'finish';
+            $headers[] = get_string('last', 'scorm');
+            $columns[] = 'score';
+            $headers[] = get_string('score', 'scorm');
+            $scoes = $DB->get_records('scorm_scoes', array("scorm"=>$scorm->id), 'id');
+            foreach ($scoes as $sco) {
+                if ($sco->launch != '') {
+                    $columns[] = 'scograde'.$sco->id;
+                    $headers[] = format_string($sco->title,'',$formattextoptions);
+                    $table->head[]= format_string($sco->title,'',$formattextoptions);
+                }
+            }
+
+            $params = array();
+            list($usql, $params) = $DB->get_in_or_equal($allowedlist);
+                                    // Construct the SQL
+            $select = 'SELECT DISTINCT '.$DB->sql_concat('u.id', '\'#\'', 'COALESCE(st.attempt, 0)').' AS uniqueid, ';
+            $select .= 'st.scormid AS scormid, st.attempt AS attempt, ' .
+                    'u.id AS userid, u.idnumber, u.firstname, u.lastname, u.picture, u.imagealt, u.email ';
+
+            // This part is the same for all cases - join users and scorm_scoes_track tables
+            $from = 'FROM {user} u ';
+            $from .= 'LEFT JOIN {scorm_scoes_track} st ON st.userid = u.id AND st.scormid = '.$scorm->id;
+            switch ($attemptsmode) {
+                case SCORM_REPORT_ATTEMPTS_STUDENTS_WITH:
+                    // Show only students with attempts
+                    $where = ' WHERE u.id ' .$usql. ' AND st.userid IS NOT NULL';
+                    break;
+                case SCORM_REPORT_ATTEMPTS_STUDENTS_WITH_NO:
+                    // Show only students without attempts
+                    $where = ' WHERE u.id ' .$usql. ' AND st.userid IS NULL';
+                    break;
+                case SCORM_REPORT_ATTEMPTS_ALL_STUDENTS:
+                    // Show all students with or without attempts
+                    $where = ' WHERE u.id ' .$usql. ' AND (st.userid IS NOT NULL OR st.userid IS NULL)';
+                    break;
+            }
+
+            $countsql = 'SELECT COUNT(DISTINCT('.$DB->sql_concat('u.id', '\'#\'', 'COALESCE(st.attempt, 0)').')) AS nbresults, ';
+            $countsql .= 'COUNT(DISTINCT('.$DB->sql_concat('u.id', '\'#\'', 'st.attempt').')) AS nbattempts, ';
+            $countsql .= 'COUNT(DISTINCT(u.id)) AS nbusers ';
+            $countsql .= $from.$where;
+            $attempts = $DB->get_records_sql($select.$from.$where, $params);
+            $questioncount = get_scorm_question_count($scorm->id);
+            for($id = 0; $id < $questioncount; $id++) {
+                if ($displayoptions['qtext']) {
+                    $columns[] = 'question' . $id;
+                    $headers[] = get_string('questionx', 'scormreport_interactions', $id);
+                }
+                if ($displayoptions['resp']) {
+                    $columns[] = 'response' . $id;
+                    $headers[] = get_string('responsex', 'scormreport_interactions', $id);
+                }
+                if ($displayoptions['right']) {
+                    $columns[] = 'right' . $id;
+                    $headers[] = get_string('rightanswerx', 'scormreport_interactions', $id);
+                }
+            }
+
+            if (!$download) {
+                $table = new flexible_table('mod-scorm-report');
+
+                $table->define_columns($columns);
+                $table->define_headers($headers);
+                $table->define_baseurl($PAGE->url);
+
+                $table->sortable(true);
+                $table->collapsible(true);
+
+                $table->column_suppress('picture');
+                $table->column_suppress('fullname');
+                $table->column_suppress('idnumber');
+
+                $table->no_sorting('start');
+                $table->no_sorting('finish');
+                $table->no_sorting('score');
+
+                foreach ($scoes as $sco) {
+                    if ($sco->launch != '') {
+                        $table->no_sorting('scograde'.$sco->id);
+                    }
+                }
+
+                $table->column_class('picture', 'picture');
+                $table->column_class('fullname', 'bold');
+                $table->column_class('score', 'bold');
+
+                $table->set_attribute('cellspacing', '0');
+                $table->set_attribute('id', 'attempts');
+                $table->set_attribute('class', 'generaltable generalbox');
+
+                // Start working -- this is necessary as soon as the niceties are over
+                $table->setup();
+            } else if ($download == 'ODS') {
+                require_once("$CFG->libdir/odslib.class.php");
+
+                $filename .= ".ods";
+                // Creating a workbook
+                $workbook = new MoodleODSWorkbook("-");
+                // Sending HTTP headers
+                $workbook->send($filename);
+                // Creating the first worksheet
+                $sheettitle = get_string('report', 'scorm');
+                $myxls =& $workbook->add_worksheet($sheettitle);
+                // format types
+                $format =& $workbook->add_format();
+                $format->set_bold(0);
+                $formatbc =& $workbook->add_format();
+                $formatbc->set_bold(1);
+                $formatbc->set_align('center');
+                $formatb =& $workbook->add_format();
+                $formatb->set_bold(1);
+                $formaty =& $workbook->add_format();
+                $formaty->set_bg_color('yellow');
+                $formatc =& $workbook->add_format();
+                $formatc->set_align('center');
+                $formatr =& $workbook->add_format();
+                $formatr->set_bold(1);
+                $formatr->set_color('red');
+                $formatr->set_align('center');
+                $formatg =& $workbook->add_format();
+                $formatg->set_bold(1);
+                $formatg->set_color('green');
+                $formatg->set_align('center');
+                // Here starts workshhet headers
+
+                $colnum = 0;
+                foreach ($headers as $item) {
+                    $myxls->write(0, $colnum, $item, $formatbc);
+                    $colnum++;
+                }
+                $rownum = 1;
+            } else if ($download =='Excel') {
+                require_once("$CFG->libdir/excellib.class.php");
+
+                $filename .= ".xls";
+                // Creating a workbook
+                $workbook = new MoodleExcelWorkbook("-");
+                // Sending HTTP headers
+                $workbook->send($filename);
+                // Creating the first worksheet
+                $sheettitle = get_string('report', 'scorm');
+                $myxls =& $workbook->add_worksheet($sheettitle);
+                // format types
+                $format =& $workbook->add_format();
+                $format->set_bold(0);
+                $formatbc =& $workbook->add_format();
+                $formatbc->set_bold(1);
+                $formatbc->set_align('center');
+                $formatb =& $workbook->add_format();
+                $formatb->set_bold(1);
+                $formaty =& $workbook->add_format();
+                $formaty->set_bg_color('yellow');
+                $formatc =& $workbook->add_format();
+                $formatc->set_align('center');
+                $formatr =& $workbook->add_format();
+                $formatr->set_bold(1);
+                $formatr->set_color('red');
+                $formatr->set_align('center');
+                $formatg =& $workbook->add_format();
+                $formatg->set_bold(1);
+                $formatg->set_color('green');
+                $formatg->set_align('center');
+
+                $colnum = 0;
+                foreach ($headers as $item) {
+                    $myxls->write(0, $colnum, $item, $formatbc);
+                    $colnum++;
+                }
+                $rownum = 1;
+            } else if ($download == 'CSV') {
+                $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 implode("\t", $headers)." \n";
+            }
+
+            if (!$download) {
+                $sort = $table->get_sql_sort();
+            } else {
+                $sort = '';
+            }
+            // Fix some wired sorting
+            if (empty($sort)) {
+                $sort = ' ORDER BY uniqueid';
+            } else {
+                $sort = ' ORDER BY '.$sort;
+            }
+
+            if (!$download) {
+                // Add extra limits due to initials bar
+                list($twhere, $tparams) = $table->get_sql_where();
+                if ($twhere) {
+                    $where .= ' AND '.$twhere; //initial bar
+                    $params = array_merge($params, $tparams);
+                }
+
+                if (!empty($countsql)) {
+                    $count = $DB->get_record_sql($countsql,$params);
+                    $totalinitials = $count->nbresults;
+                    if ($twhere) {
+                        $countsql .= ' AND '.$twhere;
+                    }
+                    $count = $DB->get_record_sql($countsql, $params);
+                    $total  = $count->nbresults;
+                }
+
+                $table->pagesize($pagesize, $total);
+
+                echo '<div class="quizattemptcounts">';
+                if ( $count->nbresults == $count->nbattempts ) {
+                    echo get_string('reportcountattempts', 'scorm', $count);
+                } else if ( $count->nbattempts>0 ) {
+                    echo get_string('reportcountallattempts', 'scorm', $count);
+                } else {
+                    echo $count->nbusers.' '.get_string('users');
+                }
+                echo '</div>';
+            }
+
+            // Fetch the attempts
+            if (!$download) {
+                $attempts = $DB->get_records_sql($select.$from.$where.$sort, $params,
+                $table->get_page_start(), $table->get_page_size());
+                echo '<div id="scormtablecontainer">';
+                if ($candelete) {
+                    // Start form
+                    $strreallydel  = addslashes_js(get_string('deleteattemptcheck', 'scorm'));
+                    echo '<form id="attemptsform" method="post" action="' . $PAGE->url->out(false) .
+                         '" onsubmit="return confirm(\''.$strreallydel.'\');">';
+                    echo '<input type="hidden" name="action" value="delete"/>';
+                    echo '<input type="hidden" name="sesskey" value="'.sesskey().'" />';
+                    echo '<div style="display: none;">';
+                    echo html_writer::input_hidden_params($PAGE->url);
+                    echo '</div>';
+                    echo '<div>';
+                }
+                $table->initialbars($totalinitials>20); // Build table rows
+            } else {
+                $attempts = $DB->get_records_sql($select.$from.$where.$sort, $params);
+            }
+            if ($attempts) {
+                foreach ($attempts as $scouser) {
+                    $row = array();
+                    if (!empty($scouser->attempt)) {
+                        $timetracks = scorm_get_sco_runtime($scorm->id, false, $scouser->userid, $scouser->attempt);
+                    } else {
+                        $timetracks = '';
+                    }
+                    if (in_array('checkbox', $columns)) {
+                        if ($candelete && !empty($timetracks->start)) {
+                            $row[] = '<input type="checkbox" name="attemptid[]" value="'. $scouser->userid . ':' . $scouser->attempt . '" />';
+                        } else if ($candelete) {
+                            $row[] = '';
+                        }
+                    }
+                    if (in_array('picture', $columns)) {
+                        $user = (object)array(
+                                    'id'=>$scouser->userid,
+                                    'picture'=>$scouser->picture,
+                                    'imagealt'=>$scouser->imagealt,
+                                    'email'=>$scouser->email,
+                                    'firstname'=>$scouser->firstname,
+                                    'lastname'=>$scouser->lastname);
+                        $row[] = $OUTPUT->user_picture($user, array('courseid'=>$course->id));
+                    }
+                    if (!$download) {
+                        $row[] = '<a href="'.$CFG->wwwroot.'/user/view.php?id='.$scouser->userid.'&amp;course='.$course->id.'">'.fullname($scouser).'</a>';
+                    } else {
+                        $row[] = fullname($scouser);
+                    }
+                    if (in_array('idnumber', $columns)) {
+                        $row[] = $scouser->idnumber;
+                    }
+                    if (empty($timetracks->start)) {
+                        $row[] = '-';
+                        $row[] = '-';
+                        $row[] = '-';
+                        $row[] = '-';
+                    } else {
+                        if (!$download) {
+                            $row[] = '<a href="userreport.php?a='.$scorm->id.'&amp;user='.$scouser->userid.'&amp;attempt='.$scouser->attempt.'">'.$scouser->attempt.'</a>';
+                        } else {
+                            $row[] = $scouser->attempt;
+                        }
+                        if ($download =='ODS' || $download =='Excel' ) {
+                            $row[] = userdate($timetracks->start, get_string("strftimedatetime", "langconfig"));
+                        } else {
+                            $row[] = userdate($timetracks->start);
+                        }
+                        if ($download =='ODS' || $download =='Excel' ) {
+                            $row[] = userdate($timetracks->finish, get_string('strftimedatetime', 'langconfig'));
+                        } else {
+                            $row[] = userdate($timetracks->finish);
+                        }
+                        $row[] = scorm_grade_user_attempt($scorm, $scouser->userid, $scouser->attempt);
+                    }
+                    // print out all scores of attempt
+                    foreach ($scoes as $sco) {
+                        if ($sco->launch != '') {
+                            if ($trackdata = scorm_get_tracks($sco->id, $scouser->userid, $scouser->attempt)) {
+                                if ($trackdata->status == '') {
+                                    $trackdata->status = 'notattempted';
+                                }
+                                $strstatus = get_string($trackdata->status, 'scorm');
+                                // if raw score exists, print it
+                                if ($trackdata->score_raw != '') {
+                                    $score = $trackdata->score_raw;
+                                    // add max score if it exists
+                                    if ($scorm->version == 'SCORM_1.3') {
+                                        $maxkey = 'cmi.score.max';
+                                    } else {
+                                        $maxkey = 'cmi.core.score.max';
+                                    }
+                                    if (isset($trackdata->$maxkey)) {
+                                        $score .= '/'.$trackdata->$maxkey;
+                                    }
+                                // else print out status
+                                } else {
+                                    $score = $strstatus;
+                                }
+                                if (!$download) {
+                                    $row[] = '<img src="'.$OUTPUT->pix_url($trackdata->status, 'scorm').'" alt="'.$strstatus.'" title="'.$strstatus.'" /><br/>
+                                            <a href="userreport.php?b='.$sco->id.'&amp;user='.$scouser->userid.'&amp;attempt='.$scouser->attempt.
+                                            '" title="'.get_string('details', 'scorm').'">'.$score.'</a>';
+                                } else {
+                                    $row[] = $score;
+                                }
+                                // interaction data
+                                $i=0;
+                                $element='cmi.interactions_'.$i.'.id';
+                                while(isset($trackdata->$element)) {
+                                    if ($displayoptions['qtext']) {
+                                        $element='cmi.interactions_'.$i.'.id';
+                                        if (isset($trackdata->$element)) {
+                                            $row[] = s($trackdata->$element);
+                                        } else {
+                                            $row[] = '&nbsp;';
+                                        }
+                                    }
+                                    if ($displayoptions['resp']) {
+                                        $element='cmi.interactions_'.$i.'.student_response';
+                                        if (isset($trackdata->$element)) {
+                                            $row[] = s($trackdata->$element);
+                                        } else {
+                                            $row[] = '&nbsp;';
+                                        }
+                                    }
+                                    if ($displayoptions['right']) {
+                                        $j=0;
+                                        $element = 'cmi.interactions_'.$i.'.correct_responses_'.$j.'.pattern';
+                                        $rightans = '';
+                                        if (isset($trackdata->$element)) {
+                                            while(isset($trackdata->$element)) {
+                                                if($j>0) {
+                                                    $rightans .= ',';
+                                                }
+                                                $rightans .= s($trackdata->$element);
+                                                $j++;
+                                                $element = 'cmi.interactions_'.$i.'.correct_responses_'.$j.'.pattern';
+                                            }
+                                            $row[] = $rightans;
+                                        } else {
+                                            $row[] = '&nbsp;';
+                                        }
+                                    }
+                                    $i++;
+                                    $element = 'cmi.interactions_'.$i.'.id';
+                                }
+                            //---end of interaction data*/
+                            } else {
+                                // if we don't have track data, we haven't attempted yet
+                                $strstatus = get_string('notattempted', 'scorm');
+                                if (!$download) {
+                                    $row[] = '<img src="'.$OUTPUT->pix_url('notattempted', 'scorm').'" alt="'.$strstatus.'" title="'.$strstatus.'" /><br/>'.$strstatus;
+                                } else {
+                                    $row[] = $strstatus;
+                                }
+                            }
+                        }
+                    }
+
+                    if (!$download) {
+                        $table->add_data($row);
+                    } else if ($download == 'Excel' or $download == 'ODS') {
+                        $colnum = 0;
+                        foreach ($row as $item) {
+                            $myxls->write($rownum, $colnum, $item, $format);
+                            $colnum++;
+                        }
+                        $rownum++;
+                    } else if ($download == 'CSV') {
+                        $text = implode("\t", $row);
+                        echo $text." \n";
+                    }
+                }
+                if (!$download) {
+                    $table->finish_output();
+                    if ($candelete) {
+                        echo '<table id="commands">';
+                        echo '<tr><td>';
+                        echo '<a href="javascript:select_all_in(\'DIV\', null, \'scormtablecontainer\');">'.
+                             get_string('selectall', 'scorm').'</a> / ';
+                        echo '<a href="javascript:deselect_all_in(\'DIV\', null, \'scormtablecontainer\');">'.
+                             get_string('selectnone', 'scorm').'</a> ';
+                        echo '&nbsp;&nbsp;';
+                        echo '<input type="submit" value="'.get_string('deleteselected', 'quiz_overview').'"/>';
+                        echo '</td></tr></table>';
+                        // Close form
+                        echo '</div>';
+                        echo '</form>';
+                    }
+                    echo '</div>';
+                    if (!empty($attempts)) {
+                        echo '<table class="boxaligncenter"><tr>';
+                        echo '<td>';
+                        echo $OUTPUT->single_button(new moodle_url($PAGE->url,
+                                                                   array('download'=>'ODS') + $displayoptions),
+                                                                   get_string('downloadods'));
+                        echo "</td>\n";
+                        echo '<td>';
+                        echo $OUTPUT->single_button(new moodle_url($PAGE->url,
+                                                                   array('download'=>'Excel') + $displayoptions),
+                                                                   get_string('downloadexcel'));
+                        echo "</td>\n";
+                        echo '<td>';
+                        echo $OUTPUT->single_button(new moodle_url($PAGE->url,
+                                                                   array('download'=>'CSV') + $displayoptions),
+                                                                   get_string('downloadtext'));
+                        echo "</td>\n";
+                        echo "<td>";
+                        echo "</td>\n";
+                        echo '</tr></table>';
+                    }
+                }
+                if (!$download) {
+                    $mform->set_data(compact('detailedrep', 'pagesize'));
+                    $mform->display();
+                }
+            } else {
+                if ($candelete && !$download) {
+                    echo '</div>';
+                    echo '</form>';
+                }
+                echo '</div>';
+                echo $OUTPUT->notification(get_string('noactivity', 'scorm'));
+            }
+            if ($download == 'Excel' or $download == 'ODS') {
+                $workbook->close();
+                exit;
+            } else if ($download == 'CSV') {
+                exit;
+            }
+        } else {
+            echo $OUTPUT->notification(get_string('noactivity', 'scorm'));
+        }
+    }// function ends
+}
diff --git a/mod/scorm/report/interactions/responsessettings_form.php b/mod/scorm/report/interactions/responsessettings_form.php
new file mode 100644 (file)
index 0000000..7a73476
--- /dev/null
@@ -0,0 +1,55 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Defines the version of scorm_interactions
+ * @package   scormreport
+ * @subpackage interactions
+ * @author    Dan Marsden and Ankit Kumar Agarwal
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+require_once("$CFG->libdir/formslib.php");
+class mod_scorm_report_interactions_settings extends moodleform {
+
+    function definition() {
+        global $COURSE;
+        $mform    =& $this->_form;
+        //-------------------------------------------------------------------------------
+        $mform->addElement('header', 'preferencespage', get_string('preferencespage', 'scorm'));
+
+        $options = array();
+        if ($COURSE->id != SITEID) {
+            $options[SCORM_REPORT_ATTEMPTS_ALL_STUDENTS] = get_string('optallstudents', 'scorm');
+            $options[SCORM_REPORT_ATTEMPTS_STUDENTS_WITH] = get_string('optattemptsonly', 'scorm');
+            $options[SCORM_REPORT_ATTEMPTS_STUDENTS_WITH_NO] = get_string('optnoattemptsonly', 'scorm');
+        }
+        $mform->addElement('select', 'attemptsmode', get_string('show', 'scorm'), $options);
+        $mform->addElement('advcheckbox', 'qtext', '',get_string('summaryofquestiontext', 'scormreport_interactions'));
+        $mform->addElement('advcheckbox', 'resp', '',get_string('summaryofresponse', 'scormreport_interactions'));
+        $mform->addElement('advcheckbox', 'right', '',get_string('summaryofrightanswer', 'scormreport_interactions'));
+
+        //-------------------------------------------------------------------------------
+        $mform->addElement('header', 'preferencesuser', get_string('preferencesuser', 'scorm'));
+
+        $mform->addElement('text', 'pagesize', get_string('pagesize', 'scorm'));
+        $mform->setType('pagesize', PARAM_INT);
+
+        $this->add_action_buttons(false, get_string('savepreferences'));
+    }
+}
diff --git a/mod/scorm/report/interactions/version.php b/mod/scorm/report/interactions/version.php
new file mode 100644 (file)
index 0000000..bb21de8
--- /dev/null
@@ -0,0 +1,29 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+
+/**
+ * Defines the version of scorm_interactions
+ * @package   scormreport
+ * @subpackage interactions
+ * @author    Dan Marsden and Ankit Kumar Agarwal
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+$plugin->version  = 2011110100;
+$plugin->requires = 2011070800;
index 7e72104..8ea5525 100644 (file)
 
 defined('MOODLE_INTERNAL') || die();
 
+/* Generates and returns list of available Scorm report sub-plugins
+ *
+ * @param context context level to check caps against
+ * @return array list of valid reports present
+ */
 function scorm_report_list($context) {
     global $CFG;
     static $reportlist;
@@ -47,3 +52,31 @@ function scorm_report_list($context) {
     }
     return $reportlist;
 }
+/**
+ * Returns The maximum numbers of Questions associated with an Scorm Pack
+ *
+ * @param int Scorm ID
+ * @return int an integer representing the question count
+ */
+function get_scorm_question_count($scormid) {
+    global $DB;
+    $count = 0;
+    $params = array();
+    $select = "scormid = ? AND ";
+    $select .= $DB->sql_like("element", "?", false);
+    $params[] = $scormid;
+    $params[] = "cmi.interactions_%.id";
+    $rs = $DB->get_recordset_select("scorm_scoes_track", $select, $params, 'element');
+    $keywords = array("cmi.interactions_", ".id");
+    foreach ($rs as $record) {
+        $num = trim(str_ireplace($keywords, '', $record->element));
+        if (is_numeric($num) && $num > $count) {
+            $count = $num;
+        }
+    }
+    //done as interactions start at 0
+    $count++;
+    $rs->close(); // closing recordset
+    return $count;
+}
+
index 6d97327..d51408d 100644 (file)
@@ -67,7 +67,7 @@ class qformat_xml extends qformat_default {
      * @param string name format name from xml file
      * @return int Moodle format code
      */
-    protected function trans_format($name) {
+    public function trans_format($name) {
         $name = trim($name);
 
         if ($name == 'moodle_auto_format') {
@@ -81,7 +81,8 @@ class qformat_xml extends qformat_default {
         } else if ($name == 'markdown') {
             return FORMAT_MARKDOWN;
         } else {
-            return 0; // or maybe warning required
+            debugging("Unrecognised text format '{$name}' in the import file. Assuming 'html'.");
+            return FORMAT_HTML;
         }
     }
 
@@ -171,7 +172,7 @@ class qformat_xml extends qformat_default {
         $qo->questiontext = $this->getpath($question,
                 array('#', 'questiontext', 0, '#', 'text', 0, '#'), '', true);
         $qo->questiontextformat = $this->trans_format($this->getpath(
-                $question, array('#', 'questiontext', 0, '@', 'format'), ''));
+                $question, array('#', 'questiontext', 0, '@', 'format'), 'html'));
 
         $qo->questiontextfiles = $this->import_files($this->getpath($question,
                 array('#', 'questiontext', 0, '#', 'file'), array(), false));
@@ -193,7 +194,7 @@ class qformat_xml extends qformat_default {
                 array('#', 'generalfeedback', 0, '#', 'text', 0, '#'), $qo->generalfeedback, true);
         $qo->generalfeedbackfiles = array();
         $qo->generalfeedbackformat = $this->trans_format($this->getpath($question,
-                array('#', 'generalfeedback', 0, '@', 'format'), 'moodle_auto_format'));
+                array('#', 'generalfeedback', 0, '@', 'format'), $this->get_format($qo->questiontextformat)));
         $qo->generalfeedbackfiles = $this->import_files($this->getpath($question,
                 array('#', 'generalfeedback', 0, '#', 'file'), array(), false));
 
@@ -223,25 +224,31 @@ class qformat_xml extends qformat_default {
     /**
      * Import the common parts of a single answer
      * @param array answer xml tree for single answer
+     * @param bool $withanswerfiles if true, the answers are HTML (or $defaultformat)
+     *      and so may contain files, otherwise the answers are plain text.
+     * @param array Default text format for the feedback, and the answers if $withanswerfiles
+     *      is true.
      * @return object answer object
      */
-    public function import_answer($answer, $withanswerfiles = false) {
+    public function import_answer($answer, $withanswerfiles = false, $defaultformat = 'html') {
         $ans = new stdClass();
 
         $ans->answer = array();
         $ans->answer['text']   = $this->getpath($answer, array('#', 'text', 0, '#'), '', true);
         $ans->answer['format'] = $this->trans_format($this->getpath($answer,
-                array('@', 'format'), 'moodle_auto_format'));
+                array('@', 'format'), $defaultformat));
         if ($withanswerfiles) {
             $ans->answer['files']  = $this->import_files($this->getpath($answer,
                     array('#', 'file'), array()));
+        } else {
+            $ans->answer['format'] = FORMAT_PLAIN;
         }
 
         $ans->feedback = array();
         $ans->feedback['text']   = $this->getpath($answer,
                 array('#', 'feedback', 0, '#', 'text', 0, '#'), '', true);
         $ans->feedback['format'] = $this->trans_format($this->getpath($answer,
-                array('#', 'feedback', 0, '@', 'format'), 'moodle_auto_format'));
+                array('#', 'feedback', 0, '@', 'format'), $defaultformat));
         $ans->feedback['files']  = $this->import_files($this->getpath($answer,
                 array('#', 'feedback', 0, '#', 'file'), array()));
 
@@ -263,7 +270,7 @@ class qformat_xml extends qformat_default {
             $text['text'] = $this->getpath($questionxml,
                     array('#', $field, 0, '#', 'text', 0, '#'), '', true);
             $text['format'] = $this->trans_format($this->getpath($questionxml,
-                    array('#', $field, 0, '@', 'format'), 'moodle_auto_format'));
+                    array('#', $field, 0, '@', 'format'), $this->get_format($qo->questiontextformat)));
             $text['files'] = $this->import_files($this->getpath($questionxml,
                     array('#', $field, 0, '#', 'file'), array(), false));
 
@@ -284,9 +291,10 @@ class qformat_xml extends qformat_default {
     /**
      * Import a question hint
      * @param array $hintxml hint xml fragment.
+     * @param string $defaultformat the text format to assume for hints that do not specify.
      * @return object hint for storing in the database.
      */
-    public function import_hint($hintxml) {
+    public function import_hint($hintxml, $defaultformat) {
         if (array_key_exists('hintcontent', $hintxml['#'])) {
             // Backwards compatibility:
 
@@ -294,6 +302,8 @@ class qformat_xml extends qformat_default {
             $hint->hint = array('format' => FORMAT_HTML, 'files' => array());
             $hint->hint['text'] = $this->getpath($hintxml,
                     array('#', 'hintcontent', 0, '#', 'text', 0, '#'), '', true);
+            $hint->hint['format'] = $this->trans_format($defaultformat);
+            $hint->hint['files'] = array();
             $hint->shownumcorrect = $this->getpath($hintxml,
                     array('#', 'statenumberofcorrectresponses', 0, '#'), 0);
             $hint->clearwrong = $this->getpath($hintxml,
@@ -308,7 +318,7 @@ class qformat_xml extends qformat_default {
         $hint->hint['text'] = $this->getpath($hintxml,
                 array('#', 'text', 0, '#'), '', true);
         $hint->hint['format'] = $this->trans_format($this->getpath($hintxml,
-                array('@', 'format'), 'moodle_auto_format'));
+                array('@', 'format'), $defaultformat));
         $hint->hint['files'] = $this->import_files($this->getpath($hintxml,
                 array('#', 'file'), array(), false));
         $hint->shownumcorrect = array_key_exists('shownumcorrect', $hintxml['#']);
@@ -322,15 +332,20 @@ class qformat_xml extends qformat_default {
      * Import all the question hints
      *
      * @param object $qo the question data that is being constructed.
-     * @param array $hintsxml hints xml fragment.
+     * @param array $questionxml The xml representing the question.
+     * @param bool $withparts whether the extra fields relating to parts should be imported.
+     * @param bool $withoptions whether the extra options field should be imported.
+     * @param string $defaultformat the text format to assume for hints that do not specify.
+     * @return array of objects representing the hints in the file.
      */
-    public function import_hints($qo, $questionxml, $withparts = false, $withoptions = false) {
+    public function import_hints($qo, $questionxml, $withparts = false,
+            $withoptions = false, $defaultformat = 'html') {
         if (!isset($questionxml['#']['hint'])) {
             return;
         }
 
         foreach ($questionxml['#']['hint'] as $hintxml) {
-            $hint = $this->import_hint($hintxml);
+            $hint = $this->import_hint($hintxml, $defaultformat);
             $qo->hint[] = $hint->hint;
 
             if ($withparts) {
@@ -390,7 +405,7 @@ class qformat_xml extends qformat_default {
         $answers = $question['#']['answer'];
         $acount = 0;
         foreach ($answers as $answer) {
-            $ans = $this->import_answer($answer, true);
+            $ans = $this->import_answer($answer, true, $this->get_format($qo->questiontextformat));
             $qo->answer[$acount] = $ans->answer;
             $qo->fraction[$acount] = $ans->fraction;
             $qo->feedback[$acount] = $ans->feedback;
@@ -398,7 +413,7 @@ class qformat_xml extends qformat_default {
         }
 
         $this->import_combined_feedback($qo, $question, true);
-        $this->import_hints($qo, $question, true);
+        $this->import_hints($qo, $question, true, false, $this->get_format($qo->questiontextformat));
 
         return $qo;
     }
@@ -412,7 +427,7 @@ class qformat_xml extends qformat_default {
         question_bank::get_qtype('multianswer');
 
         $questiontext['text'] = $this->import_text($question['#']['questiontext'][0]['#']['text']);
-        $questiontext['format'] = '1';
+        $questiontext['format'] = FORMAT_HTML;
         $questiontext['itemid'] = '';
         $qo = qtype_multianswer_extract_question($questiontext);
 
@@ -420,18 +435,20 @@ class qformat_xml extends qformat_default {
         $qo->qtype = 'multianswer';
         $qo->course = $this->course;
         $qo->generalfeedback = '';
+
+        $qo->name = $this->import_text($question['#']['name'][0]['#']['text']);
+        $qo->questiontextformat = $questiontext['format'];
+        $qo->questiontext = $qo->questiontext['text'];
+        $qo->questiontextfiles = array();
+
         // restore files in generalfeedback
         $qo->generalfeedback = $this->getpath($question,
                 array('#', 'generalfeedback', 0, '#', 'text', 0, '#'), $qo->generalfeedback, true);
         $qo->generalfeedbackformat = $this->trans_format($this->getpath($question,
-                array('#', 'generalfeedback', 0, '@', 'format'), 'moodle_auto_format'));
+                array('#', 'generalfeedback', 0, '@', 'format'), $this->get_format($qo->questiontextformat)));
         $qo->generalfeedbackfiles = $this->import_files($this->getpath($question,
                 array('#', 'generalfeedback', 0, '#', 'file'), array(), false));
 
-        $qo->name = $this->import_text($question['#']['name'][0]['#']['text']);
-        $qo->questiontext = $qo->questiontext['text'];
-        $qo->questiontextformat = '';
-
         $qo->penalty = $this->getpath($question,
                 array('#', 'penalty', 0, '#'), $this->defaultquestion()->penalty);
         // Fix problematic rounding from old files:
@@ -439,7 +456,7 @@ class qformat_xml extends qformat_default {
             $qo->penalty = 0.3333333;
         }
 
-        $this->import_hints($qo, $question);
+        $this->import_hints($qo, $question, false, false, $this->get_format($qo->questiontextformat));
 
         return $qo;
     }
@@ -469,7 +486,7 @@ class qformat_xml extends qformat_default {
             $feedback = $this->getpath($answer,
                     array('#', 'feedback', 0, '#', 'text', 0, '#'), '', true);
             $feedbackformat = $this->getpath($answer,
-                    array('#', 'feedback', 0, '@', 'format'), 'moodle_auto_format');
+                    array('#', 'feedback', 0, '@', 'format'), $this->get_format($qo->questiontextformat));
             $feedbackfiles = $this->getpath($answer,
                     array('#', 'feedback', 0, '#', 'file'), array());
             $files = array();
@@ -515,7 +532,7 @@ class qformat_xml extends qformat_default {
             echo $OUTPUT->notification(get_string('truefalseimporterror', 'qformat_xml', $a));
         }
 
-        $this->import_hints($qo, $question);
+        $this->import_hints($qo, $question, false, false, $this->get_format($qo->questiontextformat));
 
         return $qo;
     }
@@ -539,14 +556,14 @@ class qformat_xml extends qformat_default {
         $answers = $question['#']['answer'];
         $acount = 0;
         foreach ($answers as $answer) {
-            $ans = $this->import_answer($answer);
+            $ans = $this->import_answer($answer, false, $this->get_format($qo->questiontextformat));
             $qo->answer[$acount] = $ans->answer['text'];
             $qo->fraction[$acount] = $ans->fraction;
             $qo->feedback[$acount] = $ans->feedback;
             ++$acount;
         }
 
-        $this->import_hints($qo, $question);
+        $this->import_hints($qo, $question, false, false, $this->get_format($qo->questiontextformat));
 
         return $qo;
     }
@@ -586,7 +603,7 @@ class qformat_xml extends qformat_default {
         $qo->tolerance = array();
         foreach ($answers as $answer) {
             // answer outside of <text> is deprecated
-            $obj = $this->import_answer($answer);
+            $obj = $this->import_answer($answer, false, $this->get_format($qo->questiontextformat));
             $qo->answer[] = $obj->answer['text'];
             if (empty($qo->answer)) {
                 $qo->answer = '*';
@@ -622,12 +639,12 @@ class qformat_xml extends qformat_default {
             $qo->instructions['text'] = $this->getpath($instructions,
                     array('0', '#', 'text', '0', '#'), '', true);
             $qo->instructions['format'] = $this->trans_format($this->getpath($instructions,
-                    array('0', '@', 'format'), 'moodle_auto_format'));
+                    array('0', '@', 'format'), $this->get_format($qo->questiontextformat)));
             $qo->instructions['files'] = $this->import_files($this->getpath(
                     $instructions, array('0', '#', 'file'), array()));
         }
 
-        $this->import_hints($qo, $question);
+        $this->import_hints($qo, $question, false, false, $this->get_format($qo->questiontextformat));
 
         return $qo;
     }
@@ -653,7 +670,7 @@ class qformat_xml extends qformat_default {
             $subquestion = array();
             $subquestion['text'] = $this->getpath($subqxml, array('#', 'text', 0, '#'), '', true);
             $subquestion['format'] = $this->trans_format($this->getpath($subqxml,
-                    array('@', 'format'), 'moodle_auto_format'));
+                    array('@', 'format'), $this->get_format($qo->questiontextformat)));
             $subquestion['files'] = $this->import_files($this->getpath($subqxml,
                     array('#', 'file'), array()));
 
@@ -664,7 +681,7 @@ class qformat_xml extends qformat_default {
         }
 
         $this->import_combined_feedback($qo, $question, true);
-        $this->import_hints($qo, $question, true);
+        $this->import_hints($qo, $question, true, false, $this->get_format($qo->questiontextformat));
 
         return $qo;
     }
@@ -690,7 +707,7 @@ class qformat_xml extends qformat_default {
         $qo->graderinfo['text'] = $this->getpath($question,
                 array('#', 'graderinfo', 0, '#', 'text', 0, '#'), '', true);
         $qo->graderinfo['format'] = $this->trans_format($this->getpath($question,
-                array('#', 'graderinfo', 0, '@', 'format'), 'moodle_auto_format'));
+                array('#', 'graderinfo', 0, '@', 'format'), $this->get_format($qo->questiontextformat)));
         $qo->graderinfo['files'] = $this->import_files($this->getpath($question,
                 array('#', 'graderinfo', '0', '#', 'file'), array()));
 
@@ -716,30 +733,7 @@ class qformat_xml extends qformat_default {
                 array('#', 'answernumbering', 0, '#'), 'abc');
         $qo->shuffleanswers = $this->trans_single($shuffleanswers);
 
-        $qo->correctfeedback = array();
-        $qo->correctfeedback['text'] = $this->getpath(
-                $question, array('#', 'correctfeedback', 0, '#', 'text', 0, '#'), '', true);
-        $qo->correctfeedback['format'] = $this->trans_format($this->getpath(
-                $question, array('#', 'correctfeedback', 0, '@', 'format'), 'moodle_auto_format'));
-        $qo->correctfeedback['files'] = $this->import_files($this->getpath(
-                $question, array('#', 'correctfeedback', '0', '#', 'file'), array()));
-
-        $qo->partiallycorrectfeedback = array();
-        $qo->partiallycorrectfeedback['text'] = $this->getpath($question,
-                array('#', 'partiallycorrectfeedback', 0, '#', 'text', 0, '#'), '', true);
-        $qo->partiallycorrectfeedback['format'] = $this->trans_format(
-                $this->getpath($question, array('#', 'partiallycorrectfeedback', 0, '@', 'format'),
-                'moodle_auto_format'));
-        $qo->partiallycorrectfeedback['files'] = $this->import_files($this->getpath(
-                $question, array('#', 'partiallycorrectfeedback', '0', '#', 'file'), array()));
-
-        $qo->incorrectfeedback = array();
-        $qo->incorrectfeedback['text'] = $this->getpath($question,
-                array('#', 'incorrectfeedback', 0, '#', 'text', 0, '#'), '', true);
-        $qo->incorrectfeedback['format'] = $this->trans_format($this->getpath($question,
-                array('#', 'incorrectfeedback', 0, '@', 'format'), 'moodle_auto_format'));
-        $qo->incorrectfeedback['files'] = $this->import_files($this->getpath($question,
-                array('#', 'incorrectfeedback', '0', '#', 'file'), array()));
+        $this->import_combined_feedback($qo, $question);
 
         $qo->unitgradingtype = $this->getpath($question,
                 array('#', 'unitgradingtype', 0, '#'), 0);
@@ -753,7 +747,7 @@ class qformat_xml extends qformat_default {
             $qo->instructions['text'] = $this->getpath($instructions,
                     array('0', '#', 'text', '0', '#'), '', true);
             $qo->instructions['format'] = $this->trans_format($this->getpath($instructions,
-                    array('0', '@', 'format'), 'moodle_auto_format'));
+                    array('0', '@', 'format'), $this->get_format($qo->questiontextformat)));
             $qo->instructions['files'] = $this->import_files($this->getpath($instructions,
                     array('0', '#', 'file'), array()));
         }
@@ -769,7 +763,7 @@ class qformat_xml extends qformat_default {
         $qo->correctanswerlength = array();
         $qo->feedback = array();
         foreach ($answers as $answer) {
-            $ans = $this->import_answer($answer, true);
+            $ans = $this->import_answer($answer, true, $this->get_format($qo->questiontextformat));
             // answer outside of <text> is deprecated
             if (empty($ans->answer['text'])) {
                 $ans->answer['text'] = '*';
@@ -803,7 +797,7 @@ class qformat_xml extends qformat_default {
             $qo->instructions['text'] = $this->getpath($instructions,
                     array('0', '#', 'text', '0', '#'), '', true);
             $qo->instructions['format'] = $this->trans_format($this->getpath($instructions,
-                    array('0', '@', 'format'), 'moodle_auto_format'));
+                    array('0', '@', 'format'), $this->get_format($qo->questiontextformat)));
             $qo->instructions['files'] = $this->import_files($this->getpath($instructions,
                     array('0', '#', 'file'), array()));
         }
@@ -848,7 +842,7 @@ class qformat_xml extends qformat_default {
             }
         }
 
-        $this->import_hints($qo, $question);
+        $this->import_hints($qo, $question, false, false, $this->get_format($qo->questiontextformat));
 
         return $qo;
     }
@@ -970,7 +964,7 @@ class qformat_xml extends qformat_default {
      * @param int id internal code
      * @return string format text
      */
-    protected function get_format($id) {
+    public function get_format($id) {
         switch($id) {
             case FORMAT_MOODLE:
                 return 'moodle_auto_format';
index 86a8862..1aaf501 100644 (file)
@@ -191,13 +191,13 @@ END;
         $qo = new stdClass();
 
         $importer = new qformat_xml();
-        $importer->import_hints($qo, $questionxml['question']);
+        $importer->import_hints($qo, $questionxml['question'], false, false, 'html');
 
         $this->assertEqual(array(
                 array('text' => 'This is the first hint',
-                        'format' => FORMAT_MOODLE, 'files' => array()),
+                        'format' => FORMAT_HTML, 'files' => array()),
                 array('text' => 'This is the second hint',
-                        'format' => FORMAT_MOODLE, 'files' => array()),
+                        'format' => FORMAT_HTML, 'files' => array()),
                 ), $qo->hint);
         $this->assertFalse(isset($qo->hintclearwrong));
         $this->assertFalse(isset($qo->hintshownumcorrect));
@@ -221,13 +221,13 @@ END;
         $qo = new stdClass();
 
         $importer = new qformat_xml();
-        $importer->import_hints($qo, $questionxml['question'], true, true);
+        $importer->import_hints($qo, $questionxml['question'], true, true, 'html');
 
         $this->assertEqual(array(
                 array('text' => 'This is the first hint',
-                        'format' => FORMAT_MOODLE, 'files' => array()),
+                        'format' => FORMAT_HTML, 'files' => array()),
                 array('text' => 'This is the second hint',
-                        'format' => FORMAT_MOODLE, 'files' => array()),
+                        'format' => FORMAT_HTML, 'files' => array()),
                 ), $qo->hint);
         $this->assertEqual(array(1, 0), $qo->hintclearwrong);
         $this->assertEqual(array(0, 1), $qo->hintshownumcorrect);
@@ -243,7 +243,7 @@ END;
         $qo = new stdClass();
 
         $importer = new qformat_xml();
-        $importer->import_hints($qo, $questionxml['question']);
+        $importer->import_hints($qo, $questionxml['question'], 'html');
 
         $this->assertFalse(isset($qo->hint));
     }
@@ -523,27 +523,27 @@ END;
         $expectedq->questiontext = 'Match the upper and lower case letters.';
         $expectedq->questiontextformat = FORMAT_HTML;
         $expectedq->correctfeedback = array('text' => 'Well done.',
-                'format' => FORMAT_MOODLE, 'files' => array());
+                'format' => FORMAT_HTML, 'files' => array());
         $expectedq->partiallycorrectfeedback = array('text' => 'Not entirely.',
-                'format' => FORMAT_MOODLE, 'files' => array());
+                'format' => FORMAT_HTML, 'files' => array());
         $expectedq->shownumcorrect = false;
         $expectedq->incorrectfeedback = array('text' => 'Completely wrong!',
-                'format' => FORMAT_MOODLE, 'files' => array());
+                'format' => FORMAT_HTML, 'files' => array());
         $expectedq->generalfeedback = 'The answer is A -> a, B -> b and C -> c.';
-        $expectedq->generalfeedbackformat = FORMAT_MOODLE;
+        $expectedq->generalfeedbackformat = FORMAT_HTML;
         $expectedq->defaultmark = 1;
         $expectedq->length = 1;
         $expectedq->penalty = 0.3333333;
         $expectedq->shuffleanswers = 0;
         $expectedq->subquestions = array(
-            array('text' => 'A', 'format' => FORMAT_MOODLE, 'files' => array()),
-            array('text' => 'B', 'format' => FORMAT_MOODLE, 'files' => array()),
-            array('text' => 'C', 'format' => FORMAT_MOODLE, 'files' => array()),
-            array('text' => '', 'format' => FORMAT_MOODLE, 'files' => array()));
+            array('text' => 'A', 'format' => FORMAT_HTML, 'files' => array()),
+            array('text' => 'B', 'format' => FORMAT_HTML, 'files' => array()),
+            array('text' => 'C', 'format' => FORMAT_HTML, 'files' => array()),
+            array('text' => '', 'format' => FORMAT_HTML, 'files' => array()));
         $expectedq->subanswers = array('a', 'b', 'c', 'd');
         $expectedq->hint = array(
-            array('text' => 'Hint 1', 'format' => FORMAT_MOODLE, 'files' => array()),
-            array('text' => '', 'format' => FORMAT_MOODLE, 'files' => array()),
+            array('text' => 'Hint 1', 'format' => FORMAT_HTML, 'files' => array()),
+            array('text' => '', 'format' => FORMAT_HTML, 'files' => array()),
         );
         $expectedq->hintshownumcorrect = array(true, true);
         $expectedq->hintclearwrong = array(false, true);
@@ -744,17 +744,17 @@ END;
         $expectedq->questiontextformat = FORMAT_HTML;
         $expectedq->correctfeedback = array(
                 'text'   => '<p>Your answer is correct.</p>',
-                'format' => FORMAT_MOODLE,
+                'format' => FORMAT_HTML,
                 'files'  => array());
         $expectedq->shownumcorrect = false;
         $expectedq->partiallycorrectfeedback = array(
                 'text'   => '<p>Your answer is partially correct.</p>',
-                'format' => FORMAT_MOODLE,
+                'format' => FORMAT_HTML,
                 'files'  => array());
         $expectedq->shownumcorrect = true;
         $expectedq->incorrectfeedback = array(
                 'text'   => '<p>Your answer is incorrect.</p>',
-                'format' => FORMAT_MOODLE,
+                'format' => FORMAT_HTML,
                 'files'  => array());
         $expectedq->generalfeedback = 'The even numbers are 2 and 4.';
         $expectedq->defaultmark = 2;
@@ -764,20 +764,20 @@ END;
         $expectedq->single = false;
 
         $expectedq->answer = array(
-            array('text' => '1', 'format' => FORMAT_MOODLE, 'files' => array()),
-            array('text' => '2', 'format' => FORMAT_MOODLE, 'files' => array()),
-            array('text' => '3', 'format' => FORMAT_MOODLE, 'files' => array()),
-            array('text' => '4', 'format' => FORMAT_MOODLE, 'files' => array()));
+            array('text' => '1', 'format' => FORMAT_HTML, 'files' => array()),
+            array('text' => '2', 'format' => FORMAT_HTML, 'files' => array()),
+            array('text' => '3', 'format' => FORMAT_HTML, 'files' => array()),
+            array('text' => '4', 'format' => FORMAT_HTML, 'files' => array()));
         $expectedq->fraction = array(0, 1, 0, 1);
         $expectedq->feedback = array(
-            array('text' => '', 'format' => FORMAT_MOODLE, 'files' => array()),
-            array('text' => '', 'format' => FORMAT_MOODLE, 'files' => array()),
-            array('text' => '', 'format' => FORMAT_MOODLE, 'files' => array()),
-            array('text' => '', 'format' => FORMAT_MOODLE, 'files' => array()));
+            array('text' => '', 'format' => FORMAT_HTML, 'files' => array()),
+            array('text' => '', 'format' => FORMAT_HTML, 'files' => array()),
+            array('text' => '', 'format' => FORMAT_HTML, 'files' => array()),
+            array('text' => '', 'format' => FORMAT_HTML, 'files' => array()));
 
         $expectedq->hint = array(
-            array('text' => 'Hint 1.', 'format' => FORMAT_MOODLE, 'files' => array()),
-            array('text' => 'Hint 2.', 'format' => FORMAT_MOODLE, 'files' => array()),
+            array('text' => 'Hint 1.', 'format' => FORMAT_HTML, 'files' => array()),
+            array('text' => 'Hint 2.', 'format' => FORMAT_HTML, 'files' => array()),
         );
         $expectedq->hintshownumcorrect = array(false, false);
         $expectedq->hintclearwrong = array(false, false);
@@ -937,7 +937,7 @@ END;
         $expectedq->questiontext = 'What is the answer?';
         $expectedq->questiontextformat = FORMAT_HTML;
         $expectedq->generalfeedback = 'General feedback: Think Hitch-hikers guide to the Galaxy.';
-        $expectedq->generalfeedbackformat = FORMAT_MOODLE;
+        $expectedq->generalfeedbackformat = FORMAT_HTML;
         $expectedq->defaultmark = 1;
         $expectedq->length = 1;
         $expectedq->penalty = 0.1;
@@ -946,11 +946,11 @@ END;
         $expectedq->fraction = array(1, 0, 0);
         $expectedq->feedback = array(
             array('text' => 'Well done!',
-                    'format' => FORMAT_MOODLE, 'files' => array()),
+                    'format' => FORMAT_HTML, 'files' => array()),
             array('text' => 'What were you thinking?!',
-                    'format' => FORMAT_MOODLE, 'files' => array()),
+                    'format' => FORMAT_HTML, 'files' => array()),
             array('text' => 'Completely wrong.',
-                    'format' => FORMAT_MOODLE, 'files' => array()));
+                    'format' => FORMAT_HTML, 'files' => array()));
         $expectedq->tolerance = array(0.001, 1, 0);
 
         $this->assert(new CheckSpecifiedFieldsExpectation($expectedq), $q);
@@ -1081,8 +1081,8 @@ END;
         $expectedq->answer = array('Beta', '*');
         $expectedq->fraction = array(1, 0);
         $expectedq->feedback = array(
-            array('text' => 'Well done!', 'format' => FORMAT_MOODLE, 'files' => array()),
-            array('text' => 'Doh!', 'format' => FORMAT_MOODLE, 'files' => array()));
+            array('text' => 'Well done!', 'format' => FORMAT_HTML, 'files' => array()),
+            array('text' => 'Doh!', 'format' => FORMAT_HTML, 'files' => array()));
 
         $this->assert(new CheckSpecifiedFieldsExpectation($expectedq), $q);
     }
@@ -1199,9 +1199,9 @@ END;
         $expectedq->penalty = 1;
 
         $expectedq->feedbacktrue = array('text' => 'Well done!',
-                'format' => FORMAT_MOODLE, 'files' => array());
+                'format' => FORMAT_HTML, 'files' => array());
         $expectedq->feedbackfalse = array('text' => 'Doh!',
-                'format' => FORMAT_MOODLE, 'files' => array());
+                'format' => FORMAT_HTML, 'files' => array());
         $expectedq->correctanswer = true;
 
         $this->assert(new CheckSpecifiedFieldsExpectation($expectedq), $q);
index e027be6..6981dd4 100644 (file)
@@ -153,12 +153,13 @@ if (data_submitted() && confirm_sesskey()) {
 
         } else if (optional_param('fill', null, PARAM_BOOL)) {
             $correctresponse = $quba->get_correct_response($slot);
-            $quba->process_action($slot, $correctresponse);
-
-            $transaction = $DB->start_delegated_transaction();
-            question_engine::save_questions_usage_by_activity($quba);
-            $transaction->allow_commit();
+            if (!is_null($correctresponse)) {
+                $quba->process_action($slot, $correctresponse);
 
+                $transaction = $DB->start_delegated_transaction();
+                question_engine::save_questions_usage_by_activity($quba);
+                $transaction->allow_commit();
+            }
             redirect($actionurl);
 
         } else if (optional_param('finish', null, PARAM_BOOL)) {
@@ -210,6 +211,8 @@ $filldisabled = '';
 if ($quba->get_question_state($slot)->is_finished()) {
     $finishdisabled = ' disabled="disabled"';
     $filldisabled = ' disabled="disabled"';
+} else if (is_null($quba->get_correct_response($slot))) {
+    $filldisabled = ' disabled="disabled"';
 }
 if (!$previewid) {
     $restartdisabled = ' disabled="disabled"';
index 4436b10..32a908f 100644 (file)
@@ -118,7 +118,7 @@ class repository_webdav extends repository {
             }
             if (!empty($v['resourcetype']) && $v['resourcetype'] == 'collection') {
                 // a folder
-                if ($path != $v['href']) {
+                if (ltrim($path, '/') != ltrim($v['href'], '/')) {
                     $matches = array();
                     preg_match('#(\w+)$#i', $v['href'], $matches);
                     if (!empty($matches[1])) {
@@ -137,6 +137,7 @@ class repository_webdav extends repository {
                 }
             }else{
                 // a file
+                $path = rtrim($path,'/');
                 $title = urldecode(substr($v['href'], strpos($v['href'], $path)+strlen($path)));
                 $title = basename($title);
                 $size = !empty($v['getcontentlength'])? $v['getcontentlength']:'';