global $CFG, $USER, $DB;
require_once $CFG->dirroot . '/mnet/xmlrpc/client.php';
+ if (session_is_loggedinas()) {
+ print_error('notpermittedtojumpas', 'mnet');
+ }
+
// check remote login permissions
if (! has_capability('moodle/site:mnetlogintoremote', get_system_context())
or is_mnet_remote_user($USER)
* @param int $oldquestiontextformat
* @return array
*/
- protected function get_default_numerical_options($oldquestiontextformat) {
+ protected function get_default_numerical_options($oldquestiontextformat, $units) {
global $CFG;
// replay the upgrade step 2009100100 - new table
$options['instructionsformat'] = $oldquestiontextformat;
}
+ // Set a good default, depending on whether there are any units defined.
+ if (empty($units)) {
+ $options['showunits'] = 3;
+ }
+
return $options;
}
$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();
}
* @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());
$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)) {
$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
/**
* 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.
$form = $this->initialise_stage_form();
if ($form->is_cancelled()) {
- $this->ui->cancel_restore();
+ $this->ui->cancel_process();
}
$data = $form->get_data();
});
}
+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
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());
+ }
}
return false;
}
+ if (session_is_loggedinas()) {
+ $this->content = new stdClass();
+ $this->content->footer = html_writer::tag('span',
+ get_string('notpermittedtojumpas', 'mnet'));
+ return $this->content;
+ }
+
// according to start_jump_session,
// remote users can't on-jump
// so don't show this block to them
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;
}
// 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')
// 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;
//course-report-log-live not included as theres no blocks on the live log page
);
return $array;
-}
\ No newline at end of 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);
}
}
$this->add_instance($course, $fields);
+ } else {
+ if ($this->get_config('defaultenrol')) {
+ $this->add_default_instance($course);
+ }
}
} else {
$instances = $DB->get_records('enrol', array('courseid'=>$course->id, 'enrol'=>'guest'));
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';
$string['notmoodleapplication'] = 'WARNING: This is not a Moodle application, so some of the inspection methods may not work properly.';
$string['notPEM'] = 'This key is not in PEM format. It will not work.';
$string['notpermittedtojump'] = 'You do not have permission to begin a remote session from this Moodle server.';
+$string['notpermittedtojumpas'] = 'You can\'t begin a remote session while you are logged in as another user.';
$string['notpermittedtoland'] = 'You do not have permission to begin a remote session.';
$string['off'] = 'Off';
$string['on'] = 'On';
* @return string
*/
public function get_title() {
- return get_string('unenrol');
+ return get_string('unenrol', 'enrol');
}
/**
* @return string
*/
public function get_type_title() {
- return get_string('unenrol');
+ return get_string('unenrol', 'enrol');
}
/**
<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"/>
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');
}
},
/**
*/
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];
}
}
* 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
*/
private $extraclasses;
+ /**
+ * @var moodle_url full external url pointing to icon image for activity
+ */
+ private $iconurl;
+
/**
* @var string
*/
/**
* 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();
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);
$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).
$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 : '';
*/
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
+}
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...
// 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;
$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);
}
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)) {
function can_finalize($submission) {
global $USER;
+
+ if(is_bool($submission)) {
+ return false;
+ }
+
if (!$this->drafts_tracked()) {
return false;
}
$importer = new data_preset_existing_importer($course, $cm, $data, $formdata->fullname);
echo $renderer->import_setting_mappings($data, $importer);
echo $OUTPUT->footer();
+ exit(0);
} else if ($formdata = $form_importzip->get_data()) {
$file = new stdClass;
$file->name = $form_importzip->get_new_filename('importfile');
)
),
- 'mod/forum:initialsubscriptions' => array(
-
- 'captype' => 'read',
- 'contextlevel' => CONTEXT_MODULE,
- 'archetypes' => array(
- 'teacher' => CAP_ALLOW,
- 'editingteacher' => CAP_ALLOW,
- 'student' => CAP_ALLOW
- )
- ),
-
'mod/forum:postwithoutthrottling' => array(
'riskbitmask' => RISK_SPAM,
$string['forum:exportdiscussion'] = 'Export whole discussion';
$string['forum:exportownpost'] = 'Export own post';
$string['forum:exportpost'] = 'Export post';
-$string['forum:initialsubscriptions'] = 'Initial subscription';
$string['forumintro'] = 'Forum introduction';
$string['forum:managesubscriptions'] = 'Manage subscriptions';
$string['forum:movediscussions'] = 'Move discussions';
<?php
-
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
- * @package mod-forum
- * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @package mod
+ * @subpackage forum
+ * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
+defined('MOODLE_INTERNAL') || die();
+
/** Include required files */
require_once($CFG->libdir.'/filelib.php');
require_once($CFG->libdir.'/eventslib.php');
function forum_get_potential_subscribers($forumcontext, $groupid, $fields, $sort) {
global $DB;
- // only active enrolled users or everybody on the frontpage with this capability
- list($esql, $params) = get_enrolled_sql($forumcontext, 'mod/forum:initialsubscriptions', $groupid, true);
+ // only active enrolled users or everybody on the frontpage
+ list($esql, $params) = get_enrolled_sql($forumcontext, '', $groupid, true);
$sql = "SELECT $fields
FROM {user} u
/**
* This function gets run whenever user is enrolled into course
*
- * @param object $cp
+ * @param stdClass $cp
* @return void
*/
function forum_user_enrolled($cp) {
- $context = get_context_instance(CONTEXT_COURSE, $cp->courseid);
- forum_add_user_default_subscriptions($cp->userid, $context);
-}
-
-
-/**
- * This function gets run whenever user is unenrolled from course
- *
- * @param object $cp
- * @return void
- */
-function forum_user_unenrolled($cp) {
- if ($cp->lastenrol) {
- $context = get_context_instance(CONTEXT_COURSE, $cp->courseid);
- forum_remove_user_subscriptions($cp->userid, $context);
- forum_remove_user_tracking($cp->userid, $context);
- }
-}
+ global $DB;
+ // NOTE: this has to be as fast as possible - we do not want to slow down enrolments!
+ // Originally there used to be 'mod/forum:initialsubscriptions' which was
+ // introduced because we did not have enrolment information in earlier versions...
-/**
- * Add subscriptions for new users
- *
- * @global object
- * @uses CONTEXT_SYSTEM
- * @uses CONTEXT_COURSE
- * @uses CONTEXT_COURSECAT
- * @uses FORUM_INITIALSUBSCRIBE
- * @param int $userid
- * @param object $context
- * @return bool
- */
-function forum_add_user_default_subscriptions($userid, $context) {
- global $DB;
- if (empty($context->contextlevel)) {
- return false;
- }
+ $sql = "SELECT f.id
+ FROM {forum} f
+ LEFT JOIN {forum_subscriptions} fs ON (fs.forum = f.id AND fs.userid = :userid)
+ WHERE f.course = :courseid AND f.forcesubscribe = :initial AND fs.id IS NULL";
+ $params = array('courseid'=>$cp->courseid, 'userid'=>$cp->userid, 'initial'=>FORUM_INITIALSUBSCRIBE);
- switch ($context->contextlevel) {
-
- case CONTEXT_SYSTEM: // For the whole site
- $rs = $DB->get_recordset('course',null,'','id');
- foreach ($rs as $course) {
- $subcontext = get_context_instance(CONTEXT_COURSE, $course->id);
- forum_add_user_default_subscriptions($userid, $subcontext);
- }
- $rs->close();
- break;
-
- case CONTEXT_COURSECAT: // For a whole category
- $rs = $DB->get_recordset('course', array('category' => $context->instanceid),'','id');
- foreach ($rs as $course) {
- $subcontext = get_context_instance(CONTEXT_COURSE, $course->id);
- forum_add_user_default_subscriptions($userid, $subcontext);
- }
- $rs->close();
- if ($categories = $DB->get_records('course_categories', array('parent' => $context->instanceid))) {
- foreach ($categories as $category) {
- $subcontext = get_context_instance(CONTEXT_COURSECAT, $category->id);
- forum_add_user_default_subscriptions($userid, $subcontext);
- }
- }
- break;
-
-
- case CONTEXT_COURSE: // For a whole course
- if (is_enrolled($context, $userid)) {
- if ($course = $DB->get_record('course', array('id' => $context->instanceid))) {
- if ($forums = get_all_instances_in_course('forum', $course, $userid, false)) {
- foreach ($forums as $forum) {
- if ($forum->forcesubscribe != FORUM_INITIALSUBSCRIBE) {
- continue;
- }
- if ($modcontext = get_context_instance(CONTEXT_MODULE, $forum->coursemodule)) {
- if (has_capability('mod/forum:viewdiscussion', $modcontext, $userid)) {
- forum_subscribe($userid, $forum->id);
- }
- }
- }
- }
- }
- }
- break;
-
- case CONTEXT_MODULE: // Just one forum
- if (has_capability('mod/forum:initialsubscriptions', $context, $userid)) {
- if ($cm = get_coursemodule_from_id('forum', $context->instanceid)) {
- if ($forum = $DB->get_record('forum', array('id' => $cm->instance))) {
- if ($forum->forcesubscribe != FORUM_INITIALSUBSCRIBE) {
- continue;
- }
- if (has_capability('mod/forum:viewdiscussion', $context, $userid)) {
- forum_subscribe($userid, $forum->id);
- }
- }
- }
- }
- break;
+ $forums = $DB->get_records_sql($sql, $params);
+ foreach ($forums as $forum) {
+ forum_subscribe($cp->userid, $forum->id);
}
-
- return true;
}
-
/**
- * Remove subscriptions for a user in a context
+ * This function gets run whenever user is unenrolled from course
*
- * @global object
- * @global object
- * @uses CONTEXT_SYSTEM
- * @uses CONTEXT_COURSECAT
- * @uses CONTEXT_COURSE
- * @uses CONTEXT_MODULE
- * @param int $userid
- * @param object $context
- * @return bool
+ * @param stdClass $cp
+ * @return void
*/
-function forum_remove_user_subscriptions($userid, $context) {
-
- global $CFG, $DB;
-
- if (empty($context->contextlevel)) {
- return false;
- }
-
- switch ($context->contextlevel) {
+function forum_user_unenrolled($cp) {
+ global $DB;
- case CONTEXT_SYSTEM: // For the whole site
- // find all courses in which this user has a forum subscription
- if ($courses = $DB->get_records_sql("SELECT c.id
- FROM {course} c,
- {forum_subscriptions} fs,
- {forum} f
- WHERE c.id = f.course AND f.id = fs.forum AND fs.userid = ?
- GROUP BY c.id", array($userid))) {
+ // NOTE: this has to be as fast as possible!
- foreach ($courses as $course) {
- $subcontext = get_context_instance(CONTEXT_COURSE, $course->id);
- forum_remove_user_subscriptions($userid, $subcontext);
- }
- }
- break;
-
- case CONTEXT_COURSECAT: // For a whole category
- if ($courses = $DB->get_records('course', array('category' => $context->instanceid), '', 'id')) {
- foreach ($courses as $course) {
- $subcontext = get_context_instance(CONTEXT_COURSE, $course->id);
- forum_remove_user_subscriptions($userid, $subcontext);
- }
- }
- if ($categories = $DB->get_records('course_categories', array('parent' => $context->instanceid), '', 'id')) {
- foreach ($categories as $category) {
- $subcontext = get_context_instance(CONTEXT_COURSECAT, $category->id);
- forum_remove_user_subscriptions($userid, $subcontext);
- }
- }
- break;
-
- case CONTEXT_COURSE: // For a whole course
- if (!is_enrolled($context, $userid)) {
- if ($course = $DB->get_record('course', array('id' => $context->instanceid), 'id')) {
- // find all forums in which this user has a subscription, and its coursemodule id
- if ($forums = $DB->get_records_sql("SELECT f.id, cm.id as coursemodule
- FROM {forum} f,
- {modules} m,
- {course_modules} cm,
- {forum_subscriptions} fs
- WHERE fs.userid = ? AND f.course = ?
- AND fs.forum = f.id AND cm.instance = f.id
- AND cm.module = m.id AND m.name = 'forum'", array($userid, $context->instanceid))) {
-
- foreach ($forums as $forum) {
- if ($modcontext = get_context_instance(CONTEXT_MODULE, $forum->coursemodule)) {
- if (!has_capability('mod/forum:viewdiscussion', $modcontext, $userid)) {
- forum_unsubscribe($userid, $forum->id);
- }
- }
- }
- }
- }
- }
- break;
+ if ($cp->lastenrol) {
+ $params = array('userid'=>$cp->userid, 'courseid'=>$cp->courseid);
+ $forumselect = "IN (SELECT f.id FROM {forum} f WHERE f.course = :courseid)";
- case CONTEXT_MODULE: // Just one forum
- if (!is_enrolled($context, $userid)) {
- if ($cm = get_coursemodule_from_id('forum', $context->instanceid)) {
- if ($forum = $DB->get_record('forum', array('id' => $cm->instance))) {
- if (!has_capability('mod/forum:viewdiscussion', $context, $userid)) {
- forum_unsubscribe($userid, $forum->id);
- }
- }
- }
- }
- break;
+ $DB->delete_records_select('forum_subscriptions', "userid = :userid AND forum $forumselect", $params);
+ $DB->delete_records_select('forum_track_prefs', "userid = :userid AND forumid $forumselect", $params);
+ $DB->delete_records_select('forum_read', "userid = :userid AND forumid $forumselect", $params);
}
-
- return true;
}
// Functions to do with read tracking.
-/**
- * Remove post tracking for a user in a context
- *
- * @global object
- * @global object
- * @uses CONTEXT_SYSTEM
- * @uses CONTEXT_COURSECAT
- * @uses CONTEXT_COURSE
- * @uses CONTEXT_MODULE
- * @param int $userid
- * @param object $context
- * @return bool
- */
-function forum_remove_user_tracking($userid, $context) {
-
- global $CFG, $DB;
-
- if (empty($context->contextlevel)) {
- return false;
- }
-
- switch ($context->contextlevel) {
-
- case CONTEXT_SYSTEM: // For the whole site
- // find all courses in which this user has tracking info
- $allcourses = array();
- if ($courses = $DB->get_records_sql("SELECT c.id
- FROM {course} c,
- {forum_read} fr,
- {forum} f
- WHERE c.id = f.course AND f.id = fr.forumid AND fr.userid = ?
- GROUP BY c.id", array($userid))) {
-
- $allcourses = $allcourses + $courses;
- }
- if ($courses = $DB->get_records_sql("SELECT c.id
- FROM {course} c,
- {forum_track_prefs} ft,
- {forum} f
- WHERE c.id = f.course AND f.id = ft.forumid AND ft.userid = ?", array($userid))) {
-
- $allcourses = $allcourses + $courses;
- }
- foreach ($allcourses as $course) {
- $subcontext = get_context_instance(CONTEXT_COURSE, $course->id);
- forum_remove_user_tracking($userid, $subcontext);
- }
- break;
-
- case CONTEXT_COURSECAT: // For a whole category
- if ($courses = $DB->get_records('course', array('category' => $context->instanceid), '', 'id')) {
- foreach ($courses as $course) {
- $subcontext = get_context_instance(CONTEXT_COURSE, $course->id);
- forum_remove_user_tracking($userid, $subcontext);
- }
- }
- if ($categories = $DB->get_records('course_categories', array('parent' => $context->instanceid), '', 'id')) {
- foreach ($categories as $category) {
- $subcontext = get_context_instance(CONTEXT_COURSECAT, $category->id);
- forum_remove_user_tracking($userid, $subcontext);
- }
- }
- break;
-
- case CONTEXT_COURSE: // For a whole course
- if (!is_enrolled($context, $userid)) {
- if ($course = $DB->get_record('course', array('id' => $context->instanceid), 'id')) {
- // find all forums in which this user has reading tracked
- if ($forums = $DB->get_records_sql("SELECT DISTINCT f.id, cm.id as coursemodule
- FROM {forum} f,
- {modules} m,
- {course_modules} cm,
- {forum_read} fr
- WHERE fr.userid = ? AND f.course = ?
- AND fr.forumid = f.id AND cm.instance = f.id
- AND cm.module = m.id AND m.name = 'forum'", array($userid, $context->instanceid))) {
-
- foreach ($forums as $forum) {
- if ($modcontext = get_context_instance(CONTEXT_MODULE, $forum->coursemodule)) {
- if (!has_capability('mod/forum:viewdiscussion', $modcontext, $userid)) {
- forum_tp_delete_read_records($userid, -1, -1, $forum->id);
- }
- }
- }
- }
-
- // find all forums in which this user has a disabled tracking
- if ($forums = $DB->get_records_sql("SELECT f.id, cm.id as coursemodule
- FROM {forum} f,
- {modules} m,
- {course_modules} cm,
- {forum_track_prefs} ft
- WHERE ft.userid = ? AND f.course = ?
- AND ft.forumid = f.id AND cm.instance = f.id
- AND cm.module = m.id AND m.name = 'forum'", array($userid, $context->instanceid))) {
-
- foreach ($forums as $forum) {
- if ($modcontext = get_context_instance(CONTEXT_MODULE, $forum->coursemodule)) {
- if (!has_capability('mod/forum:viewdiscussion', $modcontext, $userid)) {
- $DB->delete_records('forum_track_prefs', array('userid' => $userid, 'forumid' => $forum->id));
- }
- }
- }
- }
- }
- }
- break;
-
- case CONTEXT_MODULE: // Just one forum
- if (!is_enrolled($context, $userid)) {
- if ($cm = get_coursemodule_from_id('forum', $context->instanceid)) {
- if ($forum = $DB->get_record('forum', array('id' => $cm->instance))) {
- if (!has_capability('mod/forum:viewdiscussion', $context, $userid)) {
- $DB->delete_records('forum_track_prefs', array('userid' => $userid, 'forumid' => $forum->id));
- forum_tp_delete_read_records($userid, -1, -1, $forum->id);
- }
- }
- }
- }
- break;
- }
-
- return true;
-}
-
/**
* Mark posts as read.
*
<?php
-
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
* Code fragment to define the module version etc.
* This fragment is called by /admin/index.php
*
- * @package mod-forum
- * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @package mod
+ * @subpackage forum
+ * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
-$module->version = 2011052300;
-$module->requires = 2011052300; // Requires this Moodle version
-$module->cron = 60;
\ No newline at end of file
+defined('MOODLE_INTERNAL') || die();
+
+$module->version = 2011110600;
+$module->requires = 2011110200; // Requires this Moodle version
+$module->cron = 60;
+$module->component = 'mod_forum';
\ No newline at end of file
}
$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;
}
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();
$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;
}
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';
}
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 = '';
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';
$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(
}
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;
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;
}
}
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)) {
}
}
}
- $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);
}
}
}
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";
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
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;
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
<?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
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;
}
*/
$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 ...';
$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 <username>:<activityname> 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';
$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';
* 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';
$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';
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);
} 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;
}
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;
}
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;
if (!scorm_parse_aicc($scorm)) {
$scorm->version = 'ERROR';
}
+ $scorm->version = 'AICC';
}
} else if ($scorm->scormtype === SCORM_TYPE_EXTERNAL and $cfg_scorm->allowtypeexternal) {
$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;
$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);
} 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');
}
}
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;
}
$orgstr = '¤torg='.$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);
--- /dev/null
+<?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';
+
--- /dev/null
+<?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_array('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.'&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.'&user='.$scouser->userid.'&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.'&user='.$scouser->userid.'&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[] = ' ';
+ }
+ }
+ if ($displayoptions['resp']) {
+ $element='cmi.interactions_'.$i.'.student_response';
+ if (isset($trackdata->$element)) {
+ $row[] = s($trackdata->$element);
+ } else {
+ $row[] = ' ';
+ }
+ }
+ 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[] = ' ';
+ }
+ }
+ $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 ' ';
+ 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
+}
--- /dev/null
+<?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'));
+ }
+}
--- /dev/null
+<?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;
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;
}
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;
+}
+
$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));
* @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)?
$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");
// Moodle v2.1.0 release upgrade line
// Put any upgrade step following this
+ /**
+ * Fix the eventually corrupted workshop table id sequence
+ */
+ if ($oldversion < 2011110400) {
+ $dbman->reset_sequence('workshop');
+ upgrade_mod_savepoint(true, 2011110400, 'workshop');
+ }
+
return true;
}
$DB->set_field('workshop_old', 'newid', $new->id, array('id' => $old->id));
}
$rs->close();
+ $dbman = $DB->get_manager();
+ $dbman->reset_sequence('workshop');
}
/**
defined('MOODLE_INTERNAL') || die();
-$module->version = 2011061000;
+$module->version = 2011110400;
$module->requires = 2011020900; // Requires this Moodle version
//$module->cron = 60;
* @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') {
} else if ($name == 'markdown') {
return FORMAT_MARKDOWN;
} else {
- return 0; // or maybe warning required
+ debugging("Unrecognised text format '{$name}' in the import file. Assuming 'html'.");
+ return FORMAT_HTML;
}
}
$qo->questiontext = $this->getpath($question,
array('#', 'questiontext', 0, '#', 'text', 0, '#'), '', true);
$qo->questiontextformat = $this->trans_format($this->getpath(
- $question, array('#', 'questiontext', 0, '@', 'format'), ''));
+ $question, array('#', 'questiontext', 0, '@', 'format'), 'html'));
$qo->questiontextfiles = $this->import_files($this->getpath($question,
array('#', 'questiontext', 0, '#', 'file'), array(), false));
array('#', 'generalfeedback', 0, '#', 'text', 0, '#'), $qo->generalfeedback, true);
$qo->generalfeedbackfiles = array();
$qo->generalfeedbackformat = $this->trans_format($this->getpath($question,
- array('#', 'generalfeedback', 0, '@', 'format'), 'moodle_auto_format'));
+ array('#', 'generalfeedback', 0, '@', 'format'), $this->get_format($qo->questiontextformat)));
$qo->generalfeedbackfiles = $this->import_files($this->getpath($question,
array('#', 'generalfeedback', 0, '#', 'file'), array(), false));
/**
* Import the common parts of a single answer
* @param array answer xml tree for single answer
+ * @param bool $withanswerfiles if true, the answers are HTML (or $defaultformat)
+ * and so may contain files, otherwise the answers are plain text.
+ * @param array Default text format for the feedback, and the answers if $withanswerfiles
+ * is true.
* @return object answer object
*/
- public function import_answer($answer, $withanswerfiles = false) {
+ public function import_answer($answer, $withanswerfiles = false, $defaultformat = 'html') {
$ans = new stdClass();
$ans->answer = array();
$ans->answer['text'] = $this->getpath($answer, array('#', 'text', 0, '#'), '', true);
$ans->answer['format'] = $this->trans_format($this->getpath($answer,
- array('@', 'format'), 'moodle_auto_format'));
+ array('@', 'format'), $defaultformat));
if ($withanswerfiles) {
$ans->answer['files'] = $this->import_files($this->getpath($answer,
array('#', 'file'), array()));
+ } else {
+ $ans->answer['format'] = FORMAT_PLAIN;
}
$ans->feedback = array();
$ans->feedback['text'] = $this->getpath($answer,
array('#', 'feedback', 0, '#', 'text', 0, '#'), '', true);
$ans->feedback['format'] = $this->trans_format($this->getpath($answer,
- array('#', 'feedback', 0, '@', 'format'), 'moodle_auto_format'));
+ array('#', 'feedback', 0, '@', 'format'), $defaultformat));
$ans->feedback['files'] = $this->import_files($this->getpath($answer,
array('#', 'feedback', 0, '#', 'file'), array()));
$text['text'] = $this->getpath($questionxml,
array('#', $field, 0, '#', 'text', 0, '#'), '', true);
$text['format'] = $this->trans_format($this->getpath($questionxml,
- array('#', $field, 0, '@', 'format'), 'moodle_auto_format'));
+ array('#', $field, 0, '@', 'format'), $this->get_format($qo->questiontextformat)));
$text['files'] = $this->import_files($this->getpath($questionxml,
array('#', $field, 0, '#', 'file'), array(), false));
/**
* Import a question hint
* @param array $hintxml hint xml fragment.
+ * @param string $defaultformat the text format to assume for hints that do not specify.
* @return object hint for storing in the database.
*/
- public function import_hint($hintxml) {
+ public function import_hint($hintxml, $defaultformat) {
if (array_key_exists('hintcontent', $hintxml['#'])) {
// Backwards compatibility:
$hint->hint = array('format' => FORMAT_HTML, 'files' => array());
$hint->hint['text'] = $this->getpath($hintxml,
array('#', 'hintcontent', 0, '#', 'text', 0, '#'), '', true);
+ $hint->hint['format'] = $this->trans_format($defaultformat);
+ $hint->hint['files'] = array();
$hint->shownumcorrect = $this->getpath($hintxml,
array('#', 'statenumberofcorrectresponses', 0, '#'), 0);
$hint->clearwrong = $this->getpath($hintxml,
$hint->hint['text'] = $this->getpath($hintxml,
array('#', 'text', 0, '#'), '', true);
$hint->hint['format'] = $this->trans_format($this->getpath($hintxml,
- array('@', 'format'), 'moodle_auto_format'));
+ array('@', 'format'), $defaultformat));
$hint->hint['files'] = $this->import_files($this->getpath($hintxml,
array('#', 'file'), array(), false));
$hint->shownumcorrect = array_key_exists('shownumcorrect', $hintxml['#']);
* Import all the question hints
*
* @param object $qo the question data that is being constructed.
- * @param array $hintsxml hints xml fragment.
+ * @param array $questionxml The xml representing the question.
+ * @param bool $withparts whether the extra fields relating to parts should be imported.
+ * @param bool $withoptions whether the extra options field should be imported.
+ * @param string $defaultformat the text format to assume for hints that do not specify.
+ * @return array of objects representing the hints in the file.
*/
- public function import_hints($qo, $questionxml, $withparts = false, $withoptions = false) {
+ public function import_hints($qo, $questionxml, $withparts = false,
+ $withoptions = false, $defaultformat = 'html') {
if (!isset($questionxml['#']['hint'])) {
return;
}
foreach ($questionxml['#']['hint'] as $hintxml) {
- $hint = $this->import_hint($hintxml);
+ $hint = $this->import_hint($hintxml, $defaultformat);
$qo->hint[] = $hint->hint;
if ($withparts) {
$answers = $question['#']['answer'];
$acount = 0;
foreach ($answers as $answer) {
- $ans = $this->import_answer($answer, true);
+ $ans = $this->import_answer($answer, true, $this->get_format($qo->questiontextformat));
$qo->answer[$acount] = $ans->answer;
$qo->fraction[$acount] = $ans->fraction;
$qo->feedback[$acount] = $ans->feedback;
}
$this->import_combined_feedback($qo, $question, true);
- $this->import_hints($qo, $question, true);
+ $this->import_hints($qo, $question, true, false, $this->get_format($qo->questiontextformat));
return $qo;
}
question_bank::get_qtype('multianswer');
$questiontext['text'] = $this->import_text($question['#']['questiontext'][0]['#']['text']);
- $questiontext['format'] = '1';
+ $questiontext['format'] = FORMAT_HTML;
$questiontext['itemid'] = '';
$qo = qtype_multianswer_extract_question($questiontext);
$qo->qtype = 'multianswer';
$qo->course = $this->course;
$qo->generalfeedback = '';
+
+ $qo->name = $this->import_text($question['#']['name'][0]['#']['text']);
+ $qo->questiontextformat = $questiontext['format'];
+ $qo->questiontext = $qo->questiontext['text'];
+ $qo->questiontextfiles = array();
+
// restore files in generalfeedback
$qo->generalfeedback = $this->getpath($question,
array('#', 'generalfeedback', 0, '#', 'text', 0, '#'), $qo->generalfeedback, true);
$qo->generalfeedbackformat = $this->trans_format($this->getpath($question,
- array('#', 'generalfeedback', 0, '@', 'format'), 'moodle_auto_format'));
+ array('#', 'generalfeedback', 0, '@', 'format'), $this->get_format($qo->questiontextformat)));
$qo->generalfeedbackfiles = $this->import_files($this->getpath($question,
array('#', 'generalfeedback', 0, '#', 'file'), array(), false));
- $qo->name = $this->import_text($question['#']['name'][0]['#']['text']);
- $qo->questiontext = $qo->questiontext['text'];
- $qo->questiontextformat = '';
-
$qo->penalty = $this->getpath($question,
array('#', 'penalty', 0, '#'), $this->defaultquestion()->penalty);
// Fix problematic rounding from old files:
$qo->penalty = 0.3333333;
}
- $this->import_hints($qo, $question);
+ $this->import_hints($qo, $question, false, false, $this->get_format($qo->questiontextformat));
return $qo;
}
$feedback = $this->getpath($answer,
array('#', 'feedback', 0, '#', 'text', 0, '#'), '', true);
$feedbackformat = $this->getpath($answer,
- array('#', 'feedback', 0, '@', 'format'), 'moodle_auto_format');
+ array('#', 'feedback', 0, '@', 'format'), $this->get_format($qo->questiontextformat));
$feedbackfiles = $this->getpath($answer,
array('#', 'feedback', 0, '#', 'file'), array());
$files = array();
echo $OUTPUT->notification(get_string('truefalseimporterror', 'qformat_xml', $a));
}
- $this->import_hints($qo, $question);
+ $this->import_hints($qo, $question, false, false, $this->get_format($qo->questiontextformat));
return $qo;
}
$answers = $question['#']['answer'];
$acount = 0;
foreach ($answers as $answer) {
- $ans = $this->import_answer($answer);
+ $ans = $this->import_answer($answer, false, $this->get_format($qo->questiontextformat));
$qo->answer[$acount] = $ans->answer['text'];
$qo->fraction[$acount] = $ans->fraction;
$qo->feedback[$acount] = $ans->feedback;
++$acount;
}
- $this->import_hints($qo, $question);
+ $this->import_hints($qo, $question, false, false, $this->get_format($qo->questiontextformat));
return $qo;
}
$qo->tolerance = array();
foreach ($answers as $answer) {
// answer outside of <text> is deprecated
- $obj = $this->import_answer($answer);
+ $obj = $this->import_answer($answer, false, $this->get_format($qo->questiontextformat));
$qo->answer[] = $obj->answer['text'];
if (empty($qo->answer)) {
$qo->answer = '*';
}
}
$qo->unitgradingtype = $this->getpath($question, array('#', 'unitgradingtype', 0, '#'), 0);
- $qo->unitpenalty = $this->getpath($question, array('#', 'unitpenalty', 0, '#'), 0);
- $qo->showunits = $this->getpath($question, array('#', 'showunits', 0, '#'), 0);
+ $qo->unitpenalty = $this->getpath($question, array('#', 'unitpenalty', 0, '#'), 0.1);
+ $qo->showunits = $this->getpath($question, array('#', 'showunits', 0, '#'), null);
$qo->unitsleft = $this->getpath($question, array('#', 'unitsleft', 0, '#'), 0);
$qo->instructions['text'] = '';
$qo->instructions['format'] = FORMAT_HTML;
$qo->instructions['text'] = $this->getpath($instructions,
array('0', '#', 'text', '0', '#'), '', true);
$qo->instructions['format'] = $this->trans_format($this->getpath($instructions,
- array('0', '@', 'format'), 'moodle_auto_format'));
+ array('0', '@', 'format'), $this->get_format($qo->questiontextformat)));
$qo->instructions['files'] = $this->import_files($this->getpath(
$instructions, array('0', '#', 'file'), array()));
}
- $this->import_hints($qo, $question);
+ if (is_null($qo->showunits)) {
+ // Set a good default, depending on whether there are any units defined.
+ if (empty($qo->unit)) {
+ $qo->showunits = 3; // qtype_numerical::UNITNONE;
+ } else {
+ $qo->showunits = 0; // qtype_numerical::UNITOPTIONAL;
+ }
+ }
+
+ $this->import_hints($qo, $question, false, false, $this->get_format($qo->questiontextformat));
return $qo;
}
$subquestion = array();
$subquestion['text'] = $this->getpath($subqxml, array('#', 'text', 0, '#'), '', true);
$subquestion['format'] = $this->trans_format($this->getpath($subqxml,
- array('@', 'format'), 'moodle_auto_format'));
+ array('@', 'format'), $this->get_format($qo->questiontextformat)));
$subquestion['files'] = $this->import_files($this->getpath($subqxml,
array('#', 'file'), array()));
}
$this->import_combined_feedback($qo, $question, true);
- $this->import_hints($qo, $question, true);
+ $this->import_hints($qo, $question, true, false, $this->get_format($qo->questiontextformat));
return $qo;
}
$qo->graderinfo['text'] = $this->getpath($question,
array('#', 'graderinfo', 0, '#', 'text', 0, '#'), '', true);
$qo->graderinfo['format'] = $this->trans_format($this->getpath($question,
- array('#', 'graderinfo', 0, '@', 'format'), 'moodle_auto_format'));
+ array('#', 'graderinfo', 0, '@', 'format'), $this->get_format($qo->questiontextformat)));
$qo->graderinfo['files'] = $this->import_files($this->getpath($question,
array('#', 'graderinfo', '0', '#', 'file'), array()));
array('#', 'answernumbering', 0, '#'), 'abc');
$qo->shuffleanswers = $this->trans_single($shuffleanswers);
- $qo->correctfeedback = array();
- $qo->correctfeedback['text'] = $this->getpath(
- $question, array('#', 'correctfeedback', 0, '#', 'text', 0, '#'), '', true);
- $qo->correctfeedback['format'] = $this->trans_format($this->getpath(
- $question, array('#', 'correctfeedback', 0, '@', 'format'), 'moodle_auto_format'));
- $qo->correctfeedback['files'] = $this->import_files($this->getpath(
- $question, array('#', 'correctfeedback', '0', '#', 'file'), array()));
-
- $qo->partiallycorrectfeedback = array();
- $qo->partiallycorrectfeedback['text'] = $this->getpath($question,
- array('#', 'partiallycorrectfeedback', 0, '#', 'text', 0, '#'), '', true);
- $qo->partiallycorrectfeedback['format'] = $this->trans_format(
- $this->getpath($question, array('#', 'partiallycorrectfeedback', 0, '@', 'format'),
- 'moodle_auto_format'));
- $qo->partiallycorrectfeedback['files'] = $this->import_files($this->getpath(
- $question, array('#', 'partiallycorrectfeedback', '0', '#', 'file'), array()));
-
- $qo->incorrectfeedback = array();
- $qo->incorrectfeedback['text'] = $this->getpath($question,
- array('#', 'incorrectfeedback', 0, '#', 'text', 0, '#'), '', true);
- $qo->incorrectfeedback['format'] = $this->trans_format($this->getpath($question,
- array('#', 'incorrectfeedback', 0, '@', 'format'), 'moodle_auto_format'));
- $qo->incorrectfeedback['files'] = $this->import_files($this->getpath($question,
- array('#', 'incorrectfeedback', '0', '#', 'file'), array()));
+ $this->import_combined_feedback($qo, $question);
$qo->unitgradingtype = $this->getpath($question,
array('#', 'unitgradingtype', 0, '#'), 0);
- $qo->unitpenalty = $this->getpath($question, array('#', 'unitpenalty', 0, '#'), 0);
+ $qo->unitpenalty = $this->getpath($question, array('#', 'unitpenalty', 0, '#'), null);
$qo->showunits = $this->getpath($question, array('#', 'showunits', 0, '#'), 0);
$qo->unitsleft = $this->getpath($question, array('#', 'unitsleft', 0, '#'), 0);
$qo->instructions = $this->getpath($question,
$qo->instructions['text'] = $this->getpath($instructions,
array('0', '#', 'text', '0', '#'), '', true);
$qo->instructions['format'] = $this->trans_format($this->getpath($instructions,
- array('0', '@', 'format'), 'moodle_auto_format'));
+ array('0', '@', 'format'), $this->get_format($qo->questiontextformat)));
$qo->instructions['files'] = $this->import_files($this->getpath($instructions,
array('0', '#', 'file'), array()));
}
$qo->correctanswerlength = array();
$qo->feedback = array();
foreach ($answers as $answer) {
- $ans = $this->import_answer($answer, true);
+ $ans = $this->import_answer($answer, true, $this->get_format($qo->questiontextformat));
// answer outside of <text> is deprecated
if (empty($ans->answer['text'])) {
$ans->answer['text'] = '*';
$qo->instructions['text'] = $this->getpath($instructions,
array('0', '#', 'text', '0', '#'), '', true);
$qo->instructions['format'] = $this->trans_format($this->getpath($instructions,
- array('0', '@', 'format'), 'moodle_auto_format'));
+ array('0', '@', 'format'), $this->get_format($qo->questiontextformat)));
$qo->instructions['files'] = $this->import_files($this->getpath($instructions,
array('0', '#', 'file'), array()));
}
+
+ if (is_null($qo->unitpenalty)) {
+ // Set a good default, depending on whether there are any units defined.
+ if (empty($qo->unit)) {
+ $qo->showunits = 3; // qtype_numerical::UNITNONE;
+ } else {
+ $qo->showunits = 0; // qtype_numerical::UNITOPTIONAL;
+ }
+ }
+
$datasets = $question['#']['dataset_definitions'][0]['#']['dataset_definition'];
$qo->dataset = array();
$qo->datasetindex= 0;
}
}
- $this->import_hints($qo, $question);
+ $this->import_hints($qo, $question, false, false, $this->get_format($qo->questiontextformat));
return $qo;
}
* @param int id internal code
* @return string format text
*/
- protected function get_format($id) {
+ public function get_format($id) {
switch($id) {
case FORMAT_MOODLE:
return 'moodle_auto_format';
"</shuffleanswers>\n";
$expout .= " <answernumbering>" . $question->options->answernumbering .
"</answernumbering>\n";
- $expout .= $this->write_combined_feedback($question->options);
+ $expout .= $this->write_combined_feedback($question->options, $question->id, $question->contextid);
$expout .= $this->write_answers($question->options->answers);
break;
$expout .= " <shuffleanswers>" .
$this->get_single($question->options->shuffleanswers) .
"</shuffleanswers>\n";
- $expout .= $this->write_combined_feedback($question->options);
+ $expout .= $this->write_combined_feedback($question->options, $question->id, $question->contextid);
foreach ($question->options->subquestions as $subquestion) {
$files = $fs->get_area_files($contextid, 'qtype_match',
'subquestion', $subquestion->id);
return $output;
}
+ /**
+ * Write out the hints.
+ * @param object $question the question definition data.
+ * @return string XML to output.
+ */
public function write_hints($question) {
if (empty($question->hints)) {
return '';
$output = '';
foreach ($question->hints as $hint) {
- $output .= $this->write_hint($hint);
+ $output .= $this->write_hint($hint, $question->contextid);
}
return $output;
}
return 'format="' . $this->get_format($format) . '"';
}
- public function write_hint($hint) {
+ public function write_hint($hint, $contextid) {
+ $fs = get_file_storage();
+ $files = $fs->get_area_files($contextid, 'question', 'hint', $hint->id);
+
$output = '';
$output .= " <hint {$this->format($hint->hintformat)}>\n";
$output .= ' ' . $this->writetext($hint->hint);
+
if (!empty($hint->shownumcorrect)) {
$output .= " <shownumcorrect/>\n";
}
if (!empty($hint->clearwrong)) {
$output .= " <clearwrong/>\n";
}
+
if (!empty($hint->options)) {
$output .= ' <options>' . $this->xml_escape($hint->options) . "</options>\n";
}
+ $output .= $this->writefiles($files);
$output .= " </hint>\n";
return $output;
}
- public function write_combined_feedback($questionoptions) {
- $output = " <correctfeedback {$this->format($questionoptions->correctfeedbackformat)}>
- {$this->writetext($questionoptions->correctfeedback)} </correctfeedback>
- <partiallycorrectfeedback {$this->format($questionoptions->partiallycorrectfeedbackformat)}>
- {$this->writetext($questionoptions->partiallycorrectfeedback)} </partiallycorrectfeedback>
- <incorrectfeedback {$this->format($questionoptions->incorrectfeedbackformat)}>
- {$this->writetext($questionoptions->incorrectfeedback)} </incorrectfeedback>\n";
+ /**
+ * Output the combined feedback fields.
+ * @param object $questionoptions the question definition data.
+ * @param int $questionid the question id.
+ * @param int $contextid the question context id.
+ * @return string XML to output.
+ */
+ public function write_combined_feedback($questionoptions, $questionid, $contextid) {
+ $fs = get_file_storage();
+ $output = '';
+
+ $fields = array('correctfeedback', 'partiallycorrectfeedback', 'incorrectfeedback');
+ foreach ($fields as $field) {
+ $formatfield = $field . 'format';
+ $files = $fs->get_area_files($contextid, 'question', $field, $questionid);
+
+ $output .= " <{$field} {$this->format($questionoptions->$formatfield)}>\n";
+ $output .= ' ' . $this->writetext($questionoptions->$field);
+ $output .= $this->writefiles($files);
+ $output .= " </{$field}>\n";
+ }
+
if (!empty($questionoptions->shownumcorrect)) {
$output .= " <shownumcorrect/>\n";
}
$qo = new stdClass();
$importer = new qformat_xml();
- $importer->import_hints($qo, $questionxml['question']);
+ $importer->import_hints($qo, $questionxml['question'], false, false, 'html');
$this->assertEqual(array(
array('text' => 'This is the first hint',
- 'format' => FORMAT_MOODLE, 'files' => array()),
+ 'format' => FORMAT_HTML, 'files' => array()),
array('text' => 'This is the second hint',
- 'format' => FORMAT_MOODLE, 'files' => array()),
+ 'format' => FORMAT_HTML, 'files' => array()),
), $qo->hint);
$this->assertFalse(isset($qo->hintclearwrong));
$this->assertFalse(isset($qo->hintshownumcorrect));
$qo = new stdClass();
$importer = new qformat_xml();
- $importer->import_hints($qo, $questionxml['question'], true, true);
+ $importer->import_hints($qo, $questionxml['question'], true, true, 'html');
$this->assertEqual(array(
array('text' => 'This is the first hint',
- 'format' => FORMAT_MOODLE, 'files' => array()),
+ 'format' => FORMAT_HTML, 'files' => array()),
array('text' => 'This is the second hint',
- 'format' => FORMAT_MOODLE, 'files' => array()),
+ 'format' => FORMAT_HTML, 'files' => array()),
), $qo->hint);
$this->assertEqual(array(1, 0), $qo->hintclearwrong);
$this->assertEqual(array(0, 1), $qo->hintshownumcorrect);
$qo = new stdClass();
$importer = new qformat_xml();
- $importer->import_hints($qo, $questionxml['question']);
+ $importer->import_hints($qo, $questionxml['question'], 'html');
$this->assertFalse(isset($qo->hint));
}
$expectedq->questiontext = 'Match the upper and lower case letters.';
$expectedq->questiontextformat = FORMAT_HTML;
$expectedq->correctfeedback = array('text' => 'Well done.',
- 'format' => FORMAT_MOODLE, 'files' => array());
+ 'format' => FORMAT_HTML, 'files' => array());
$expectedq->partiallycorrectfeedback = array('text' => 'Not entirely.',
- 'format' => FORMAT_MOODLE, 'files' => array());
+ 'format' => FORMAT_HTML, 'files' => array());
$expectedq->shownumcorrect = false;
$expectedq->incorrectfeedback = array('text' => 'Completely wrong!',
- 'format' => FORMAT_MOODLE, 'files' => array());
+ 'format' => FORMAT_HTML, 'files' => array());
$expectedq->generalfeedback = 'The answer is A -> a, B -> b and C -> c.';
- $expectedq->generalfeedbackformat = FORMAT_MOODLE;
+ $expectedq->generalfeedbackformat = FORMAT_HTML;
$expectedq->defaultmark = 1;
$expectedq->length = 1;
$expectedq->penalty = 0.3333333;
$expectedq->shuffleanswers = 0;
$expectedq->subquestions = array(
- array('text' => 'A', 'format' => FORMAT_MOODLE, 'files' => array()),
- array('text' => 'B', 'format' => FORMAT_MOODLE, 'files' => array()),
- array('text' => 'C', 'format' => FORMAT_MOODLE, 'files' => array()),
- array('text' => '', 'format' => FORMAT_MOODLE, 'files' => array()));
+ array('text' => 'A', 'format' => FORMAT_HTML, 'files' => array()),
+ array('text' => 'B', 'format' => FORMAT_HTML, 'files' => array()),
+ array('text' => 'C', 'format' => FORMAT_HTML, 'files' => array()),
+ array('text' => '', 'format' => FORMAT_HTML, 'files' => array()));
$expectedq->subanswers = array('a', 'b', 'c', 'd');
$expectedq->hint = array(
- array('text' => 'Hint 1', 'format' => FORMAT_MOODLE, 'files' => array()),
- array('text' => '', 'format' => FORMAT_MOODLE, 'files' => array()),
+ array('text' => 'Hint 1', 'format' => FORMAT_HTML, 'files' => array()),
+ array('text' => '', 'format' => FORMAT_HTML, 'files' => array()),
);
$expectedq->hintshownumcorrect = array(true, true);
$expectedq->hintclearwrong = array(false, true);
$expectedq->questiontextformat = FORMAT_HTML;
$expectedq->correctfeedback = array(
'text' => '<p>Your answer is correct.</p>',
- 'format' => FORMAT_MOODLE,
+ 'format' => FORMAT_HTML,
'files' => array());
$expectedq->shownumcorrect = false;
$expectedq->partiallycorrectfeedback = array(
'text' => '<p>Your answer is partially correct.</p>',
- 'format' => FORMAT_MOODLE,
+ 'format' => FORMAT_HTML,
'files' => array());
$expectedq->shownumcorrect = true;
$expectedq->incorrectfeedback = array(
'text' => '<p>Your answer is incorrect.</p>',
- 'format' => FORMAT_MOODLE,
+ 'format' => FORMAT_HTML,
'files' => array());
$expectedq->generalfeedback = 'The even numbers are 2 and 4.';
$expectedq->defaultmark = 2;
$expectedq->single = false;
$expectedq->answer = array(
- array('text' => '1', 'format' => FORMAT_MOODLE, 'files' => array()),
- array('text' => '2', 'format' => FORMAT_MOODLE, 'files' => array()),
- array('text' => '3', 'format' => FORMAT_MOODLE, 'files' => array()),
- array('text' => '4', 'format' => FORMAT_MOODLE, 'files' => array()));
+ array('text' => '1', 'format' => FORMAT_HTML, 'files' => array()),
+ array('text' => '2', 'format' => FORMAT_HTML, 'files' => array()),
+ array('text' => '3', 'format' => FORMAT_HTML, 'files' => array()),
+ array('text' => '4', 'format' => FORMAT_HTML, 'files' => array()));
$expectedq->fraction = array(0, 1, 0, 1);
$expectedq->feedback = array(
- array('text' => '', 'format' => FORMAT_MOODLE, 'files' => array()),
- array('text' => '', 'format' => FORMAT_MOODLE, 'files' => array()),
- array('text' => '', 'format' => FORMAT_MOODLE, 'files' => array()),
- array('text' => '', 'format' => FORMAT_MOODLE, 'files' => array()));
+ array('text' => '', 'format' => FORMAT_HTML, 'files' => array()),
+ array('text' => '', 'format' => FORMAT_HTML, 'files' => array()),
+ array('text' => '', 'format' => FORMAT_HTML, 'files' => array()),
+ array('text' => '', 'format' => FORMAT_HTML, 'files' => array()));
$expectedq->hint = array(
- array('text' => 'Hint 1.', 'format' => FORMAT_MOODLE, 'files' => array()),
- array('text' => 'Hint 2.', 'format' => FORMAT_MOODLE, 'files' => array()),
+ array('text' => 'Hint 1.', 'format' => FORMAT_HTML, 'files' => array()),
+ array('text' => 'Hint 2.', 'format' => FORMAT_HTML, 'files' => array()),
);
$expectedq->hintshownumcorrect = array(false, false);
$expectedq->hintclearwrong = array(false, false);
$expectedq->questiontext = 'What is the answer?';
$expectedq->questiontextformat = FORMAT_HTML;
$expectedq->generalfeedback = 'General feedback: Think Hitch-hikers guide to the Galaxy.';
- $expectedq->generalfeedbackformat = FORMAT_MOODLE;
+ $expectedq->generalfeedbackformat = FORMAT_HTML;
$expectedq->defaultmark = 1;
$expectedq->length = 1;
$expectedq->penalty = 0.1;
$expectedq->fraction = array(1, 0, 0);
$expectedq->feedback = array(
array('text' => 'Well done!',
- 'format' => FORMAT_MOODLE, 'files' => array()),
+ 'format' => FORMAT_HTML, 'files' => array()),
array('text' => 'What were you thinking?!',
- 'format' => FORMAT_MOODLE, 'files' => array()),
+ 'format' => FORMAT_HTML, 'files' => array()),
array('text' => 'Completely wrong.',
- 'format' => FORMAT_MOODLE, 'files' => array()));
+ 'format' => FORMAT_HTML, 'files' => array()));
$expectedq->tolerance = array(0.001, 1, 0);
$this->assert(new CheckSpecifiedFieldsExpectation($expectedq), $q);
$expectedq->answer = array('Beta', '*');
$expectedq->fraction = array(1, 0);
$expectedq->feedback = array(
- array('text' => 'Well done!', 'format' => FORMAT_MOODLE, 'files' => array()),
- array('text' => 'Doh!', 'format' => FORMAT_MOODLE, 'files' => array()));
+ array('text' => 'Well done!', 'format' => FORMAT_HTML, 'files' => array()),
+ array('text' => 'Doh!', 'format' => FORMAT_HTML, 'files' => array()));
$this->assert(new CheckSpecifiedFieldsExpectation($expectedq), $q);
}
$expectedq->penalty = 1;
$expectedq->feedbacktrue = array('text' => 'Well done!',
- 'format' => FORMAT_MOODLE, 'files' => array());
+ 'format' => FORMAT_HTML, 'files' => array());
$expectedq->feedbackfalse = array('text' => 'Doh!',
- 'format' => FORMAT_MOODLE, 'files' => array());
+ 'format' => FORMAT_HTML, 'files' => array());
$expectedq->correctanswer = true;
$this->assert(new CheckSpecifiedFieldsExpectation($expectedq), $q);
} 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)) {
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"';
// convert and write the numerical units and numerical options
if (isset($data['calculated'][0]['numerical_units'])) {
- $numericalunits = $data['calculated'][0]['numerical_units'];
- $numericaloptions = $this->get_default_numerical_options($data['oldquestiontextformat']);
+ $numericalunits = $data['calculated'][0]['numerical_units'];
} else {
- $numericalunits = array();
- $numericaloptions = array();
+ $numericalunits = array();
}
+ $numericaloptions = $this->get_default_numerical_options(
+ $data['oldquestiontextformat'], $numericalunits);
+
$this->write_numerical_units($numericalunits);
$this->write_numerical_options($numericaloptions);
// convert and write the numerical units and numerical options
if (isset($data['numerical'][0]['numerical_units'])) {
- $numericalunits = $data['numerical'][0]['numerical_units'];
- $numericaloptions = $this->get_default_numerical_options($data['oldquestiontextformat']);
+ $numericalunits = $data['numerical'][0]['numerical_units'];
} else {
- $numericalunits = array();
- $numericaloptions = array();
+ $numericalunits = array();
}
+ $numericaloptions = $this->get_default_numerical_options(
+ $data['oldquestiontextformat'], $numericalunits);
+
$this->write_numerical_units($numericalunits);
$this->write_numerical_options($numericaloptions);
<FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="true" NEXT="question"/>
<FIELD NAME="question" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="id" NEXT="showunits"/>
- <FIELD NAME="showunits" TYPE="int" LENGTH="4" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" COMMENT="display units as multichoice" PREVIOUS="question" NEXT="unitsleft"/>
+ <FIELD NAME="showunits" TYPE="int" LENGTH="4" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" COMMENT="How units are handled: 3) Not used at all, 0) Optional, or 1) must be right or penalty applied." PREVIOUS="question" NEXT="unitsleft"/>
<FIELD NAME="unitsleft" TYPE="int" LENGTH="4" NOTNULL="true" UNSIGNED="false" DEFAULT="0" SEQUENCE="false" COMMENT="display the unit at left as in $1.00" PREVIOUS="showunits" NEXT="unitgradingtype"/>
- <FIELD NAME="unitgradingtype" TYPE="int" LENGTH="4" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" COMMENT="0 no penalty, 1 response grade, 2 total grade" PREVIOUS="unitsleft" NEXT="unitpenalty"/>
- <FIELD NAME="unitpenalty" TYPE="number" LENGTH="12" NOTNULL="true" UNSIGNED="true" DEFAULT="0.1" SEQUENCE="false" DECIMALS="7" PREVIOUS="unitgradingtype"/>
+ <FIELD NAME="unitgradingtype" TYPE="int" LENGTH="4" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" COMMENT="0 no penalty, 1 fraction response grade, 2 fraction total grade" PREVIOUS="unitsleft" NEXT="unitpenalty"/>
+ <FIELD NAME="unitpenalty" TYPE="number" LENGTH="12" NOTNULL="true" UNSIGNED="true" DEFAULT="0.1" SEQUENCE="false" DECIMALS="7" COMMENT="Penalty for getting the unit wrong, when they are being graded." PREVIOUS="unitgradingtype"/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id" NEXT="question"/>
// Adding keys to table question_numerical_options
$table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
$table->add_key('question', XMLDB_KEY_FOREIGN, array('question'), 'question', array('id'));
+
// Conditionally launch create table for question_calculated_options
if (!$dbman->table_exists($table)) {
// $dbman->create_table doesnt return a result, we just have to trust it
$dbman->create_table($table);
}
+
+ // Set a better default for questions without units.
+ $DB->execute('
+ UPDATE {question_numerical_options} qno
+ SET showunits = 3
+ WHERE NOT EXISTS (
+ SELECT 1
+ FROM {question_numerical_units} qnu
+ WHERE qnu.question = qno.question)');
+
upgrade_plugin_savepoint(true, 2009100100, 'qtype', 'numerical');
}
$dbman->add_field($table, $field);
}
- // In the past, question_match_sub.questiontext assumed to contain
+ // In the past, question_numerical_options.instructions assumed to contain
// content of the same form as question.questiontextformat. If we are
// using the HTML editor, then convert FORMAT_MOODLE content to FORMAT_HTML.
$rs = $DB->get_recordset_sql('
--- /dev/null
+This files describes API changes for question type plugins.
+
+=== 2.2 ===
+
+* The XML import/export base class has had some minor API changes. The
+ - write_combined_feedback method now requires that you pass the questionid and
+ contextid. (MDL-29058)
+ - calls to the import_hints and import_answer methods now should pass the question
+ text format as the last argument, to be used as a default if necessary. (MDL-29739)
+ If you do not upgrade your code, it will not break, but there will be PHP
+ warnings, and it the export will not work 100% correctly.
+
+* Question type plugins should start using a string called 'pluginname' for the
+ question type name, as with other plugins. Using a string with the same name
+ as the question type (e.g. get_string('essay', 'qtype_essay') will be supported
+ for one more release, but will generate a debugging warning.
\ No newline at end of file
}
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])) {
}
}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']:'';