Merge branch 'MDL-29058' of git://github.com/timhunt/moodle
authorAparup Banerjee <aparup@moodle.com>
Wed, 9 Nov 2011 03:29:45 +0000 (11:29 +0800)
committerAparup Banerjee <aparup@moodle.com>
Wed, 9 Nov 2011 03:29:45 +0000 (11:29 +0800)
42 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
course/report/participation/index.php
lang/en/backup.php
lib/filestorage/file_types.mm
lib/form/form.js
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/aicc.php
mod/scorm/datamodels/aicclib.php
mod/scorm/db/install.xml
mod/scorm/db/upgrade.php
mod/scorm/lang/en/scorm.php
mod/scorm/lib.php
mod/scorm/loadSCO.php
mod/scorm/locallib.php
mod/scorm/mod_form.php
mod/scorm/module.js
mod/scorm/player.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
mod/scorm/settings.php
mod/scorm/version.php
mod/scorm/view.php
question/format/xml/format.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 659b198..d66fd79 100644 (file)
 
         $countsql = "SELECT COUNT(DISTINCT(ra.userid))
                        FROM {role_assignments} ra
+                       JOIN {user} u ON u.id = ra.userid
                       WHERE ra.contextid $relatedcontexts AND ra.roleid = :roleid";
 
         $totalcount = $DB->count_records_sql($countsql, $params);
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 0b69586..ee473eb 100644 (file)
@@ -30,6 +30,7 @@ M.form.initShowAdvanced = function(Y, config) {
             if (this._advButtons.size() > 0) {
                 this._stateInput = new Y.NodeList(document.getElementsByName('mform_showadvanced_last'));
                 this._advButtons.on('click', this.switchState, this);
+                this._advButtons.set('type', 'button');
             }
         },
         /**
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 40ce4a8..ea61f39 100644 (file)
@@ -33,32 +33,47 @@ $command = required_param('command', PARAM_ALPHA);
 $sessionid = required_param('session_id', PARAM_ALPHANUM);
 $aiccdata = optional_param('aicc_data', '', PARAM_RAW);
 
+$cfg_scorm = get_config('scorm');
+
 $url = new moodle_url('/mod/scorm/aicc.php', array('command'=>$command, 'session_id'=>$sessionid));
 if ($aiccdata !== 0) {
     $url->param('aicc_data', $aiccdata);
 }
 $PAGE->set_url($url);
 
-require_login();
+if (empty($cfg_scorm->allowaicchacp)) {
+    require_login();
+    if (!confirm_sesskey($sessionid)) {
+        print_error('invalidsesskey');
+    }
+    $aiccuser = $USER;
+    $scormsession = $SESSION->scorm;
+} else {
+    $scormsession = scorm_aicc_confirm_hacp_session($sessionid);
+    if (empty($scormsession)) {
+        print_error('invalidhacpsession', 'scorm');
+    }
+    $aiccuser = $DB->get_record('user', array('id'=>$scormsession->userid), 'id,username,lastname,firstname', MUST_EXIST);
+}
 
-if (!empty($command) && confirm_sesskey($sessionid)) {
+if (!empty($command)) {
     $command = strtolower($command);
 
-    if (isset($SESSION->scorm_scoid)) {
-        $scoid = $SESSION->scorm_scoid;
+    if (isset($scormsession->scoid)) {
+        $scoid = $scormsession->scoid;
     } else {
         print_error('cannotcallscript');
     }
     $mode = 'normal';
-    if (isset($SESSION->scorm_mode)) {
-        $mode = $SESSION->scorm_mode;
+    if (isset($scormsession->scormmode)) {
+        $mode = $scormsession->scormmode;
     }
     $status = 'Not Initialized';
-    if (isset($SESSION->scorm_status)) {
-        $status = $SESSION->scorm_status;
+    if (isset($scormsession->scormstatus)) {
+        $status = $scormsession->scormstatus;
     }
-    if (isset($SESSION->scorm_attempt)) {
-        $attempt = $SESSION->scorm_attempt;
+    if (isset($scormsession->attempt)) {
+        $attempt = $scormsession->attempt;
     } else {
         $attempt = 1;
     }
@@ -84,20 +99,20 @@ if (!empty($command) && confirm_sesskey($sessionid)) {
         switch ($command) {
             case 'getparam':
                 if ($status == 'Not Initialized') {
-                    $SESSION->scorm_status = 'Running';
+                    $scormsession->scormstatus = 'Running';
                     $status = 'Running';
                 }
                 if ($status != 'Running') {
                     echo "error=101\r\nerror_text=Terminated\r\n";
                 } else {
-                    if ($usertrack=scorm_get_tracks($scoid, $USER->id, $attempt)) {
+                    if ($usertrack=scorm_get_tracks($scoid, $aiccuser->id, $attempt)) {
                         $userdata = $usertrack;
                     } else {
                         $userdata->status = '';
                         $userdata->score_raw = '';
                     }
-                    $userdata->student_id = $USER->username;
-                    $userdata->student_name = $USER->lastname .', '. $USER->firstname;
+                    $userdata->student_id = $aiccuser->username;
+                    $userdata->student_name = $aiccuser->lastname .', '. $aiccuser->firstname;
                     $userdata->mode = $mode;
                     if ($userdata->mode == 'normal') {
                         $userdata->credit = 'credit';
@@ -135,10 +150,10 @@ if (!empty($command) && confirm_sesskey($sessionid)) {
                         }
                         if (isset($userdata->{'cmi.core.lesson_status'})) {
                             echo 'Lesson_Status='.$userdata->{'cmi.core.lesson_status'}.$userdata->entry."\r\n";
-                            $SESSION->scorm_lessonstatus = $userdata->{'cmi.core.lesson_status'};
+                            $scormsession->scorm_lessonstatus = $userdata->{'cmi.core.lesson_status'};
                         } else {
                             echo 'Lesson_Status=not attempted'.$userdata->entry."\r\n";
-                            $SESSION->scorm_lessonstatus = 'not attempted';
+                            $scormsession->scorm_lessonstatus = 'not attempted';
                         }
                         if (isset($userdata->{'cmi.core.score.raw'})) {
                             $max = '';
@@ -183,8 +198,8 @@ if (!empty($command) && confirm_sesskey($sessionid)) {
                     if (!empty($aiccdata) && has_capability('mod/scorm:savetrack', get_context_instance(CONTEXT_MODULE, $cm->id))) {
                         $initlessonstatus = 'not attempted';
                         $lessonstatus = 'not attempted';
-                        if (isset($SESSION->scorm_lessonstatus)) {
-                            $initlessonstatus = $SESSION->scorm_lessonstatus;
+                        if (isset($scormsession->scorm_lessonstatus)) {
+                            $initlessonstatus = $scormsession->scorm_lessonstatus;
                         }
                         $score = '';
                         $datamodel['lesson_location'] = 'cmi.core.lesson_location';
@@ -203,7 +218,7 @@ if (!empty($command) && confirm_sesskey($sessionid)) {
                                     $element = $datamodel[$element];
                                     switch ($element) {
                                         case 'cmi.core.lesson_location':
-                                            $id = scorm_insert_track($USER->id, $scorm->id, $sco->id, $attempt, $element, $value);
+                                            $id = scorm_insert_track($aiccuser->id, $scorm->id, $sco->id, $attempt, $element, $value);
                                         break;
                                         case 'cmi.core.lesson_status':
                                             $statuses = array(
@@ -239,13 +254,13 @@ if (!empty($command) && confirm_sesskey($sessionid)) {
                                             }
                                             if (empty($value) || isset($exites[$value])) {
                                                 $subelement = 'cmi.core.exit';
-                                                $id = scorm_insert_track($USER->id, $scorm->id, $sco->id, $attempt, $subelement, $value);
+                                                $id = scorm_insert_track($aiccuser->id, $scorm->id, $sco->id, $attempt, $subelement, $value);
                                             }
                                             $value = trim(strtolower($values[0]));
                                             $value = $value[0];
                                             if (isset($statuses[$value]) && ($mode == 'normal')) {
                                                 $value = $statuses[$value];
-                                                $id = scorm_insert_track($USER->id, $scorm->id, $sco->id, $attempt, $element, $value);
+                                                $id = scorm_insert_track($aiccuser->id, $scorm->id, $sco->id, $attempt, $element, $value);
                                             }
                                             $lessonstatus = $value;
                                         break;
@@ -254,23 +269,23 @@ if (!empty($command) && confirm_sesskey($sessionid)) {
                                             if ((count($values) > 1) && ($values[1] >= $values[0]) && is_numeric($values[1])) {
                                                 $subelement = 'cmi.core.score.max';
                                                 $value = trim($values[1]);
-                                                $id = scorm_insert_track($USER->id, $scorm->id, $sco->id, $attempt, $subelement, $value);
+                                                $id = scorm_insert_track($aiccuser->id, $scorm->id, $sco->id, $attempt, $subelement, $value);
                                                 if ((count($values) == 3) && ($values[2] <= $values[0]) && is_numeric($values[2])) {
                                                     $subelement = 'cmi.core.score.min';
                                                     $value = trim($values[2]);
-                                                    $id = scorm_insert_track($USER->id, $scorm->id, $sco->id, $attempt, $subelement, $value);
+                                                    $id = scorm_insert_track($aiccuser->id, $scorm->id, $sco->id, $attempt, $subelement, $value);
                                                 }
                                             }
 
                                             $value = '';
                                             if (is_numeric($values[0])) {
                                                 $value = trim($values[0]);
-                                                $id = scorm_insert_track($USER->id, $scorm->id, $sco->id, $attempt, $element, $value);
+                                                $id = scorm_insert_track($aiccuser->id, $scorm->id, $sco->id, $attempt, $element, $value);
                                             }
                                             $score = $value;
                                         break;
                                         case 'cmi.core.session_time':
-                                             $SESSION->scorm_session_time = $value;
+                                             $scormsession->sessiontime = $value;
                                         break;
                                     }
                                 }
@@ -283,13 +298,13 @@ if (!empty($command) && confirm_sesskey($sessionid)) {
                                         next($datarows);
                                     }
                                     $value = rawurlencode($value);
-                                    $id = scorm_insert_track($USER->id, $scorm->id, $sco->id, $attempt, $element, $value);
+                                    $id = scorm_insert_track($aiccuser->id, $scorm->id, $sco->id, $attempt, $element, $value);
                                 }
                             }
                         }
                         if (($mode == 'browse') && ($initlessonstatus == 'not attempted')) {
                             $lessonstatus = 'browsed';
-                            $id = scorm_insert_track($USER->id, $scorm->id, $sco->id, $attempt, 'cmi.core.lesson_status', 'browsed');
+                            $id = scorm_insert_track($aiccuser->id, $scorm->id, $sco->id, $attempt, 'cmi.core.lesson_status', 'browsed');
                         }
                         if ($mode == 'normal') {
                             if ($sco = scorm_get_sco($scoid)) {
@@ -302,7 +317,7 @@ if (!empty($command) && confirm_sesskey($sessionid)) {
                                         }
                                     }
                                 }
-                                $id = scorm_insert_track($USER->id, $scorm->id, $sco->id, $attempt, 'cmi.core.lesson_status', $lessonstatus);
+                                $id = scorm_insert_track($aiccuser->id, $scorm->id, $sco->id, $attempt, 'cmi.core.lesson_status', $lessonstatus);
                             }
                         }
                     }
@@ -360,32 +375,32 @@ if (!empty($command) && confirm_sesskey($sessionid)) {
             break;
             case 'exitau':
                 if ($status == 'Running') {
-                    if (isset($SESSION->scorm_session_time) && ($SESSION->scorm_session_time != '')) {
-                        if ($track = $DB->get_record('scorm_scoes_track', array("userid"=>$USER->id,
+                    if (isset($scormsession->sessiontime) && ($scormsession->sessiontime != '')) {
+                        if ($track = $DB->get_record('scorm_scoes_track', array("userid"=>$aiccuser->id,
                                                                                 "scormid"=>$scorm->id,
                                                                                 "scoid"=>$sco->id,
                                                                                 "attempt"=>$attempt,
                                                                                 "element"=>'cmi.core.total_time'))) {
                             // Add session_time to total_time
-                            $value = scorm_add_time($track->value, $SESSION->scorm_session_time);
+                            $value = scorm_add_time($track->value, $scormsession->sessiontime);
                             $track->value = $value;
                             $track->timemodified = time();
                             $DB->update_record('scorm_scoes_track', $track);
                         } else {
                             $track = new stdClass();
-                            $track->userid = $USER->id;
+                            $track->userid = $aiccuser->id;
                             $track->scormid = $scorm->id;
                             $track->scoid = $sco->id;
                             $track->element = 'cmi.core.total_time';
-                            $track->value = $SESSION->scorm_session_time;
+                            $track->value = $scormsession->sessiontime;
                             $track->attempt = $attempt;
                             $track->timemodified = time();
                             $id = $DB->insert_record('scorm_scoes_track', $track);
                         }
-                        scorm_update_grades($scorm, $USER->id);
+                        scorm_update_grades($scorm, $aiccuser->id);
                     }
-                    $SESSION->scorm_status = 'Terminated';
-                    $SESSION->scorm_session_time = '';
+                    $scormsession->scormstatus = 'Terminated';
+                    $scormsession->session_time = '';
                     echo "error=0\r\nerror_text=Successful\r\n";
                 } else if ($status == 'Terminated') {
                     echo "error=1\r\nerror_text=Terminated\r\n";
@@ -405,6 +420,13 @@ if (!empty($command) && confirm_sesskey($sessionid)) {
         echo "error=3\r\nerror_text=Invalid Session ID\r\n";
     }
 }
+if (empty($cfg_scorm->allowaicchacp)) {
+    $SESSION->scorm = $scormsession;
+} else {
+    $scormsession->timemodified = time();
+    $DB->update_record('scorm_aicc_session', $scormsession);
+}
+
 $aiccresponse = ob_get_contents();
 scorm_debug_log_write("aicc", "HACP Response:\r\n$aiccresponse", $scoid);
 ob_end_flush();
\ No newline at end of file
index 016a55a..de43634 100644 (file)
@@ -110,6 +110,9 @@ function scorm_forge_cols_regexp($columns, $remodule='(".*")?,') {
 function scorm_parse_aicc($scorm) {
     global $DB;
 
+    if ($scorm->scormtype == SCORM_TYPE_AICCURL) {
+        return scorm_aicc_generate_simple_sco($scorm);
+    }
     if (!isset($scorm->cmid)) {
         $cm = get_coursemodule_from_instance('scorm', $scorm->id);
         $scorm->cmid = $cm->id;
@@ -341,3 +344,95 @@ function scorm_parse_aicc($scorm) {
 
     return true;
 }
+
+/**
+ * Given a scormid creates an AICC Session record to allow HACP
+ *
+ * @param int $scormid - id from scorm table
+ * @return string hacpsession
+ */
+function scorm_aicc_get_hacp_session($scormid) {
+    global $USER, $DB, $SESSION;
+    $cfg_scorm = get_config('scorm');
+    if (empty($cfg_scorm->allowaicchacp)) {
+        return false;
+    }
+    $now = time();
+
+    $hacpsession = $SESSION->scorm;
+    $hacpsession->scormid = $scormid;
+    $hacpsession->hacpsession = random_string(20);
+    $hacpsession->userid      = $USER->id;
+    $hacpsession->timecreated = $now;
+    $hacpsession->timemodified = $now;
+    $DB->insert_record('scorm_aicc_session', $hacpsession);
+
+    return $hacpsession->hacpsession;
+}
+
+/**
+ * Check the hacp_session for whether it is valid.
+ *
+ * @param string $hacpsession The hacpsession value to check (optional). Normally leave this blank
+ *      and this function will do required_param('sesskey', ...).
+ * @return mixed - false if invalid, otherwise returns record from scorm_aicc_session table.
+ */
+function scorm_aicc_confirm_hacp_session($hacpsession) {
+    global $DB;
+    $cfg_scorm = get_config('scorm');
+    if (empty($cfg_scorm->allowaicchacp)) {
+        return false;
+    }
+    $time = time()-($cfg_scorm->aicchacptimeout * 60);
+    $sql = "hacpsession = ? AND timemodified > ?";
+    $hacpsession = $DB->get_record_select('scorm_aicc_session', $sql, array($hacpsession, $time));
+    if (!empty($hacpsession)) { //update timemodified as this is still an active session - resets the timeout.
+        $hacpsession->timemodified = time();
+        $DB->update_record('scorm_aicc_session', $hacpsession);
+    }
+    return $hacpsession;
+}
+
+/**
+ * generate a simple single activity AICC object
+ * structure to wrap around and externally linked
+ * AICC package URL
+ *
+ * @param object $scorm package record
+ */
+function scorm_aicc_generate_simple_sco($scorm) {
+    global $DB;
+    // find the old one
+    $scos = $DB->get_records('scorm_scoes', array('scorm'=>$scorm->id));
+    if (!empty($scos)) {
+        $sco = array_shift($scos);
+    } else {
+        $sco = new object();
+    }
+    // get rid of old ones
+    foreach($scos as $oldsco) {
+        $DB->delete_records('scorm_scoes', array('id'=>$oldsco->id));
+        $DB->delete_records('scorm_scoes_track', array('scoid'=>$oldsco->id));
+    }
+
+    $sco->identifier = 'A1';
+    $sco->scorm = $scorm->id;
+    $sco->organization = '';
+    $sco->title = $scorm->name;
+    $sco->parent = '/';
+    // add the HACP signal to the activity launcher
+    if (preg_match('/\?/', $scorm->reference)) {
+        $sco->launch = $scorm->reference.'&CMI=HACP';
+    }
+    else {
+        $sco->launch = $scorm->reference.'?CMI=HACP';
+    }
+    $sco->scormtype = 'sco';
+    if (isset($sco->id)) {
+        $DB->update_record('scorm_scoes', $sco);
+        $id = $sco->id;
+    } else {
+        $id = $DB->insert_record('scorm_scoes', $sco);
+    }
+    return $id;
+}
\ No newline at end of file
index ca356ba..fefbb5f 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8" ?>
-<XMLDB PATH="mod/scorm/db" VERSION="20110731" COMMENT="XMLDB file for Moodle mod/scorm"
+<XMLDB PATH="mod/scorm/db" VERSION="20111105" COMMENT="XMLDB file for Moodle mod/scorm"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:noNamespaceSchemaLocation="../../../lib/xmldb/xmldb.xsd"
 >
         <KEY NAME="scorm_rolluprule_scoid" TYPE="foreign" FIELDS="scoid" REFTABLE="scorm_scoes" REFFIELDS="id" COMMENT="The relative sco" PREVIOUS="scorm_rolluprule_uniq"/>
       </KEYS>
     </TABLE>
-    <TABLE NAME="scorm_seq_rolluprulecond" COMMENT="SCORM2004 sequencing rule" PREVIOUS="scorm_seq_rolluprule">
+    <TABLE NAME="scorm_seq_rolluprulecond" COMMENT="SCORM2004 sequencing rule" PREVIOUS="scorm_seq_rolluprule" NEXT="scorm_aicc_session">
       <FIELDS>
         <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="true" NEXT="scoid"/>
         <FIELD NAME="scoid" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="id" NEXT="rollupruleid"/>
         <KEY NAME="scorm_rolluprulecond_rolluprule" TYPE="foreign" FIELDS="rollupruleid" REFTABLE="scorm_seq_rolluprule" REFFIELDS="id" COMMENT="The relative rolluprule" PREVIOUS="scorm_rolluprulecond_scoid"/>
       </KEYS>
     </TABLE>
+      <TABLE NAME="scorm_aicc_session" COMMENT="Used by AICC HACP to store session information" PREVIOUS="scorm_seq_rolluprulecond">
+        <FIELDS>
+          <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="true" NEXT="userid"/>
+          <FIELD NAME="userid" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" COMMENT="id from user table" PREVIOUS="id" NEXT="scormid"/>
+          <FIELD NAME="scormid" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" COMMENT="id from scorm table" PREVIOUS="userid" NEXT="hacpsession"/>
+          <FIELD NAME="hacpsession" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" COMMENT="sessionid used to authenticate AICC HACP communication" PREVIOUS="scormid" NEXT="scoid"/>
+          <FIELD NAME="scoid" TYPE="int" LENGTH="10" NOTNULL="false" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" COMMENT="id from scorm_scoes table" PREVIOUS="hacpsession" NEXT="scormmode"/>
+          <FIELD NAME="scormmode" TYPE="char" LENGTH="50" NOTNULL="false" SEQUENCE="false" PREVIOUS="scoid" NEXT="scormstatus"/>
+          <FIELD NAME="scormstatus" TYPE="char" LENGTH="255" NOTNULL="false" SEQUENCE="false" PREVIOUS="scormmode" NEXT="attempt"/>
+          <FIELD NAME="attempt" TYPE="int" LENGTH="10" NOTNULL="false" UNSIGNED="true" SEQUENCE="false" PREVIOUS="scormstatus" NEXT="lessonstatus"/>
+          <FIELD NAME="lessonstatus" TYPE="char" LENGTH="255" NOTNULL="false" SEQUENCE="false" PREVIOUS="attempt" NEXT="sessiontime"/>
+          <FIELD NAME="sessiontime" TYPE="char" LENGTH="255" NOTNULL="false" SEQUENCE="false" PREVIOUS="lessonstatus" NEXT="timecreated"/>
+          <FIELD NAME="timecreated" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" COMMENT="time this session was created" PREVIOUS="sessiontime" NEXT="timemodified"/>
+          <FIELD NAME="timemodified" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" COMMENT="time this session was last used" PREVIOUS="timecreated"/>
+        </FIELDS>
+        <KEYS>
+          <KEY NAME="primary" TYPE="primary" FIELDS="id" NEXT="scormid"/>
+          <KEY NAME="scormid" TYPE="foreign" FIELDS="scormid" REFTABLE="scorm" REFFIELDS="id" PREVIOUS="primary" NEXT="userid"/>
+          <KEY NAME="userid" TYPE="foreign" FIELDS="userid" REFTABLE="user" REFFIELDS="id" PREVIOUS="scormid"/>
+        </KEYS>
+      </TABLE>
+
   </TABLES>
 </XMLDB>
\ No newline at end of file
index 2335654..32396c9 100644 (file)
@@ -571,6 +571,39 @@ function xmldb_scorm_upgrade($oldversion) {
 
         upgrade_mod_savepoint(true, 2011080100, 'scorm');
     }
+    if ($oldversion < 2011110502) {
+
+        // Define table scorm_aicc_session to be created
+        $table = new xmldb_table('scorm_aicc_session');
+
+        // Adding fields to table scorm_aicc_session
+        $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
+        $table->add_field('userid', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
+        $table->add_field('scormid', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
+        $table->add_field('hacpsession', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null);
+        $table->add_field('scoid', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, null, null, '0');
+        $table->add_field('scormmode', XMLDB_TYPE_CHAR, '50', null, null, null, null);
+        $table->add_field('scormstatus', XMLDB_TYPE_CHAR, '255', null, null, null, null);
+        $table->add_field('attempt', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, null, null, null);
+        $table->add_field('lessonstatus', XMLDB_TYPE_CHAR, '255', null, null, null, null);
+        $table->add_field('sessiontime', XMLDB_TYPE_CHAR, '255', null, null, null, null);
+        $table->add_field('timecreated', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
+        $table->add_field('timemodified', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
+
+        // Adding keys to table scorm_aicc_session
+        $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
+        $table->add_key('scormid', XMLDB_KEY_FOREIGN, array('scormid'), 'scorm', array('id'));
+        $table->add_key('userid', XMLDB_KEY_FOREIGN, array('userid'), 'user', array('id'));
+
+        // Conditionally launch create table for scorm_aicc_session
+        if (!$dbman->table_exists($table)) {
+            $dbman->create_table($table);
+        }
+
+        // scorm savepoint reached
+        upgrade_mod_savepoint(true, 2011110502, 'scorm');
+    }
+
     return true;
 }
 
index b0a55f0..5ca3aa8 100644 (file)
  */
 $string['toc'] = 'TOC';
 $string['navigation'] = 'Navigation';
-
+$string['aicchacptimeout'] = 'AICC HACP Timeout';
+$string['aicchacptimeout_desc'] = 'Length of time in minutes that an external AICC HACP session can remain open';
+$string['aicchacpkeepsessiondata'] = 'AICC HACP session data';
+$string['aicchacpkeepsessiondata_desc'] = 'Length of time in days to keep the external AICC HACP session data (a high setting will fill up the table with old data but may be useful when debugging)';
 $string['activation'] = 'Activation';
 $string['activityloading'] = 'You will be automatically redirected to the activity in';
 $string['activitypleasewait'] = 'Activity loading, please wait ...';
@@ -31,14 +34,18 @@ $string['adminsettings'] = 'Admin settings';
 $string['advanced'] = 'Parameters';
 $string['allowapidebug'] = 'Activate API debug and tracing (set the capture mask with apidebugmask)';
 $string['allowtypeexternal'] = 'Enable external package type';
+$string['allowtypeexternalaicc'] = 'Enable direct AICC url';
+$string['allowtypeexternalaicc_desc'] = 'If enabled this allows a direct url to a simple AICC package';
 $string['allowtypeimsrepository'] = 'Enable IMS package type';
 $string['allowtypelocalsync'] = 'Enable downloaded package type';
+$string['allowtypeaicchacp'] = 'Enable external AICC HACP';
+$string['allowtypeaicchacp_desc'] = 'If enabled this allows AICC HACP external communication without requiring user login for post requests from the external AICC package';
 $string['apidebugmask'] = 'API debug capture mask  - use a simple regex on &lt;username&gt;:&lt;activityname&gt; e.g. admin:.* will debug for admin user only';
 $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';
@@ -161,6 +168,8 @@ $string['interactionstype'] = 'Type of question';
 $string['interactionsweight'] = 'Weight assigned to the element';
 $string['interactionslearnerresponse'] = 'Learner\'s Response';
 $string['invalidactivity'] = 'Scorm activity is incorrect';
+$string['invalidurl'] = 'Invalid URL specified';
+$string['invalidhacpsession'] = 'Invalid HACP Session';
 $string['invalidmanifestresource'] = 'WARNING: The following resources were referenced in your manifest but couldn\'t be found:';
 $string['last'] = 'Last accessed on';
 $string['lastaccess'] = 'Last access';
@@ -259,7 +268,8 @@ $string['scormtype_help'] = 'This setting determines how the package is included
 * Uploaded package - Enables a SCORM package to be chosen via the file picker
 * External SCORM manifest - Enables an imsmanifest.xml URL to be specified. Note: If the URL has a different domain name than your site, then "Downloaded package" is a better option, since otherwise grades are not saved.
 * Downloaded package - Enables a package URL to be specified. The package will be unzipped and saved locally, and updated when the external SCORM package is updated.
-* Local IMS content repository - Enables a package to be selected from within an IMS repository';
+* Local IMS content repository - Enables a package to be selected from within an IMS repository
+* External AICC URL - this URL is the launch URL for a single AICC Activity.  A psuedo package will be constructed around this.';
 $string['scorm:viewreport'] = 'View reports';
 $string['scorm:viewscores'] = 'View scores';
 $string['scrollbars'] = 'Allow the window to be scrolled';
@@ -291,6 +301,7 @@ $string['too_many_children'] = 'Tag {$a->tag} has too many children';
 $string['totaltime'] = 'Time';
 $string['trackingloose'] = 'WARNING: The tracking data of this package will be lost!';
 $string['type'] = 'Type';
+$string['typeaiccurl'] = 'External AICC URL';
 $string['typeexternal'] = 'External SCORM manifest';
 $string['typeimsrepository'] = 'Local IMS content repository';
 $string['typelocal'] = 'Uploaded package';
index 16fcb32..b4c84a9 100644 (file)
@@ -28,6 +28,8 @@ define('SCORM_TYPE_LOCALSYNC', 'localsync');
 define('SCORM_TYPE_EXTERNAL', 'external');
 /** SCORM_TYPE_IMSREPOSITORY = imsrepository */
 define('SCORM_TYPE_IMSREPOSITORY', 'imsrepository');
+/** SCORM_TYPE_AICCURL = external AICC url */
+define('SCORM_TYPE_AICCURL', 'aiccurl');
 
 define('SCORM_TOC_SIDE', 0);
 define('SCORM_TOC_HIDDEN', 1);
@@ -103,13 +105,12 @@ function scorm_add_instance($scorm, $mform=null) {
 
     } else if ($record->scormtype === SCORM_TYPE_LOCALSYNC) {
         $record->reference = $scorm->packageurl;
-
     } else if ($record->scormtype === SCORM_TYPE_EXTERNAL) {
         $record->reference = $scorm->packageurl;
-
     } else if ($record->scormtype === SCORM_TYPE_IMSREPOSITORY) {
         $record->reference = $scorm->packageurl;
-
+    } else if ($record->scormtype === SCORM_TYPE_AICCURL) {
+        $record->reference = $scorm->packageurl;
     } else {
         return false;
     }
@@ -504,6 +505,13 @@ function scorm_cron () {
         foreach ($scormsupdate as $scormupdate) {
             scorm_parse($scormupdate, true);
         }
+
+        //now clear out AICC session table with old session data
+        $cfg_scorm = get_config('scorm');
+        if (!empty($cfg_scorm->allowaicchacp)) {
+            $expiretime = time() - ($cfg_scorm->aicchacpkeepsessiondata*24*60*60);
+            $DB->delete_records_select('scorm_aicc_session', 'WHERE timemodified < ?', array($expiretime));
+        }
     }
 
     return true;
index 057ea84..d7988f5 100644 (file)
@@ -108,11 +108,16 @@ if ((isset($sco->parameters) && (!empty($sco->parameters))) || ($version == 'AIC
 }
 
 if ($version == 'AICC') {
+    require_once("$CFG->dirroot/mod/scorm/datamodels/aicclib.php");
+    $aicc_sid = scorm_aicc_get_hacp_session($scorm->id);
+    if (empty($aicc_sid)) {
+        $aicc_sid = sesskey();
+    }
     $sco_params = '';
     if (isset($sco->parameters) && (!empty($sco->parameters))) {
         $sco_params = '&'. $sco->parameters;
     }
-    $launcher = $sco->launch.$connector.'aicc_sid='.sesskey().'&aicc_url='.$CFG->wwwroot.'/mod/scorm/aicc.php'.$sco_params;
+    $launcher = $sco->launch.$connector.'aicc_sid='.$aicc_sid.'&aicc_url='.$CFG->wwwroot.'/mod/scorm/aicc.php'.$sco_params;
 } else {
     if (isset($sco->parameters) && (!empty($sco->parameters))) {
         $launcher = $sco->launch.$connector.$sco->parameters;
index b9f2619..727b872 100644 (file)
@@ -244,6 +244,7 @@ function scorm_parse($scorm, $full) {
             if (!scorm_parse_aicc($scorm)) {
                 $scorm->version = 'ERROR';
             }
+            $scorm->version = 'AICC';
         }
 
     } else if ($scorm->scormtype === SCORM_TYPE_EXTERNAL and $cfg_scorm->allowtypeexternal) {
@@ -266,7 +267,13 @@ function scorm_parse($scorm, $full) {
             $scorm->version = 'ERROR';
         }
         $newhash = sha1($scorm->reference);
-
+    } else if ($scorm->scormtype === SCORM_TYPE_AICCURL  and $cfg_scorm->allowtypeexternalaicc) {
+        require_once("$CFG->dirroot/mod/scorm/datamodels/aicclib.php");
+        // AICC
+        if (!scorm_parse_aicc($scorm)) {
+            $scorm->version = 'ERROR';
+        }
+        $scorm->version = 'AICC';
     } else {
         // sorry, disabled type
         return;
index 98dda8b..1f17c00 100644 (file)
@@ -62,6 +62,10 @@ class mod_scorm_mod_form extends moodleform_mod {
             $options[SCORM_TYPE_IMSREPOSITORY] = get_string('typeimsrepository', 'scorm');
         }
 
+        if ($cfg_scorm->allowtypeexternalaicc) {
+            $options[SCORM_TYPE_AICCURL] = get_string('typeaiccurl', 'scorm');
+        }
+
         // Reference
         if (count($options) > 1) {
             $mform->addElement('select', 'scormtype', get_string('scormtype', 'scorm'), $options);
@@ -360,19 +364,24 @@ class mod_scorm_mod_form extends moodleform_mod {
         } else if ($type === SCORM_TYPE_EXTERNAL) {
             $reference = $data['packageurl'];
             if (!preg_match('/(http:\/\/|https:\/\/|www).*\/imsmanifest.xml$/i', $reference)) {
-                $errors['packageurl'] = get_string('required'); // TODO: improve help
+                $errors['packageurl'] = get_string('invalidurl', 'scorm');
             }
 
         } else if ($type === 'packageurl') {
             $reference = $data['reference'];
             if (!preg_match('/(http:\/\/|https:\/\/|www).*(\.zip|\.pif)$/i', $reference)) {
-                $errors['packageurl'] = get_string('required'); // TODO: improve help
+                $errors['packageurl'] = get_string('invalidurl', 'scorm');
             }
 
         } else if ($type === SCORM_TYPE_IMSREPOSITORY) {
             $reference = $data['packageurl'];
             if (stripos($reference, '#') !== 0) {
-                $errors['packageurl'] = get_string('required');
+                $errors['packageurl'] = get_string('invalidurl', 'scorm');
+            }
+        } else if ($type === SCORM_TYPE_AICCURL) {
+            $reference = $data['packageurl'];
+            if (!preg_match('/(http:\/\/|https:\/\/|www).*/', $reference)) {
+                $errors['packageurl'] = get_string('invalidurl', 'scorm');
             }
         }
 
index a3d5877..de48e11 100644 (file)
@@ -48,6 +48,24 @@ M.mod_scorm.init = function(Y, hide_nav, hide_toc, toc_title, window_name, launc
 
     Y.use('yui2-resize', 'yui2-dragdrop', 'yui2-container', 'yui2-button', 'yui2-layout', 'yui2-treeview', 'yui2-json', 'yui2-event', function(Y) {
 
+        YAHOO.widget.TextNode.prototype.getContentHtml = function() {
+            var sb = [];
+            sb[sb.length] = this.href ? '<a' : '<span';
+            sb[sb.length] = ' id="' + YAHOO.lang.escapeHTML(this.labelElId) + '"';
+            sb[sb.length] = ' class="' + YAHOO.lang.escapeHTML(this.labelStyle) + '"';
+            if (this.href) {
+                sb[sb.length] = ' href="' + YAHOO.lang.escapeHTML(this.href) + '"';
+                sb[sb.length] = ' target="' + YAHOO.lang.escapeHTML(this.target) + '"';
+            }
+            if (this.title) {
+                sb[sb.length] = ' title="' + YAHOO.lang.escapeHTML(this.title) + '"';
+            }
+            sb[sb.length] = ' >';
+            sb[sb.length] = this.label;
+            sb[sb.length] = this.href?'</a>':'</span>';
+            return sb.join("");
+        };
+
         var scorm_activate_item = function(node) {
             if (!node) {
                 return;
index 4399d23..0b9a806 100644 (file)
@@ -170,10 +170,10 @@ if ($mode == 'browse') {
 }
 $orgstr = '&currentorg='.$currentorg;
 
-$SESSION->scorm_scoid = $sco->id;
-$SESSION->scorm_status = 'Not Initialized';
-$SESSION->scorm_mode = $mode;
-$SESSION->scorm_attempt = $attempt;
+$SESSION->scorm->scoid = $sco->id;
+$SESSION->scorm->scormstatus = 'Not Initialized';
+$SESSION->scorm->scormmode = $mode;
+$SESSION->scorm->attempt = $attempt;
 
 // Mark module viewed
 $completion = new completion_info($course);
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 90463b1..e45d0ce 100644 (file)
@@ -123,6 +123,18 @@ if ($ADMIN->fulltree) {
 
     $settings->add(new admin_setting_configcheckbox('scorm/allowtypeimsrepository', get_string('allowtypeimsrepository', 'scorm'), '', 0));
 
+    $settings->add(new admin_setting_configcheckbox('scorm/allowtypeexternalaicc', get_string('allowtypeexternalaicc', 'scorm'), get_string('allowtypeexternalaicc_desc', 'scorm'), 0));
+
+    $settings->add(new admin_setting_configcheckbox('scorm/allowaicchacp', get_string('allowtypeaicchacp', 'scorm'), get_string('allowtypeaicchacp_desc', 'scorm'), 0));
+
+    $settings->add(new admin_setting_configtext('scorm/aicchacptimeout',
+        get_string('aicchacptimeout', 'scorm'), get_string('aicchacptimeout_desc', 'scorm'),
+        30, PARAM_INT));
+
+    $settings->add(new admin_setting_configtext('scorm/aicchacpkeepsessiondata',
+        get_string('aicchacpkeepsessiondata', 'scorm'), get_string('aicchacpkeepsessiondata_desc', 'scorm'),
+        1, PARAM_INT));
+
     $settings->add(new admin_setting_configcheckbox('scorm/forcejavascript', get_string('forcejavascript', 'scorm'), get_string('forcejavascript_desc', 'scorm'), 1));
 
     $settings->add(new admin_setting_configcheckbox('scorm/allowapidebug', get_string('allowapidebug', 'scorm'), '', 0));
index 59dfec0..52a0c19 100644 (file)
@@ -23,6 +23,6 @@
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
-$module->version  = 2011080100;   // The (date) version of this module
+$module->version  = 2011110502;   // The (date) version of this module
 $module->requires = 2010080300;   // The version of Moodle that is required
 $module->cron     = 300;            // How often should cron check this module (seconds)?
index 43b80f0..2b7775d 100644 (file)
@@ -60,8 +60,8 @@ require_login($course->id, false, $cm);
 $context = get_context_instance(CONTEXT_COURSE, $course->id);
 $contextmodule = get_context_instance(CONTEXT_MODULE, $cm->id);
 
-if (isset($SESSION->scorm_scoid)) {
-    unset($SESSION->scorm_scoid);
+if (isset($SESSION->scorm)) {
+    unset($SESSION->scorm);
 }
 
 $strscorms = get_string("modulenameplural", "scorm");
index 9da3511..c6974c6 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') {
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']:'';