Skip to content

Commit

Permalink
MDL-27469 scorm: Add extra activity completion conditions
Browse files Browse the repository at this point in the history
  • Loading branch information
Aaron Barnes committed Jul 13, 2011
1 parent e62a947 commit 45fb80c
Show file tree
Hide file tree
Showing 9 changed files with 278 additions and 5 deletions.
17 changes: 17 additions & 0 deletions lib/completionlib.php
Original file line number Diff line number Diff line change
Expand Up @@ -1315,6 +1315,23 @@ function internal_get_grade_state($item, $grade) {
}
}

/**
* Aggregate activity completion state
*
* @access public
* @param int $type Aggregation type (COMPLETION_* constant)
* @param bool $old Old state
* @param bool $new New state
* @return bool
*/
public static function aggregate_completion_states($type, $old, $new) {
if ($type == COMPLETION_AND) {
return $old && $new;
} else {
return $old || $new;
}
}

/**
* This is to be used only for system errors (things that shouldn't happen)
* and not user-level errors.
Expand Down
3 changes: 2 additions & 1 deletion mod/scorm/backup/moodle2/backup_scorm_stepslib.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ protected function define_structure() {
'sha1hash', 'md5hash', 'revision', 'launch',
'skipview', 'hidebrowse', 'hidetoc', 'hidenav',
'auto', 'popup', 'options', 'width',
'height', 'timeopen', 'timeclose', 'timemodified'));
'height', 'timeopen', 'timeclose', 'timemodified',
'completionstatusrequired', 'completionscorerequired'));

$scoes = new backup_nested_element('scoes');

Expand Down
4 changes: 3 additions & 1 deletion mod/scorm/db/install.xml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@
<FIELD NAME="height" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="600" SEQUENCE="false" PREVIOUS="width" NEXT="timeopen"/>
<FIELD NAME="timeopen" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="height" NEXT="timeclose"/>
<FIELD NAME="timeclose" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="timeopen" NEXT="timemodified"/>
<FIELD NAME="timemodified" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="timeclose"/>
<FIELD NAME="timemodified" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="timeclose" NEXT="completionstatusrequired"/>
<FIELD NAME="completionstatusrequired" TYPE="int" LENGTH="1" NOTNULL="false" UNSIGNED="true" DEFAULT="null" SEQUENCE="false" PREVIOUS="timemodified" NEXT="completionscorerequired"/>
<FIELD NAME="completionscorerequired" TYPE="int" LENGTH="2" NOTNULL="false" UNSIGNED="true" DEFAULT="null" SEQUENCE="false" PREVIOUS="completionstatusrequired"/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
Expand Down
17 changes: 17 additions & 0 deletions mod/scorm/db/upgrade.php
Original file line number Diff line number Diff line change
Expand Up @@ -547,6 +547,23 @@ function xmldb_scorm_upgrade($oldversion) {
upgrade_mod_savepoint(true, 2011021402, 'scorm');
}

// Adding completion fields to scorm table
if ($oldversion < 2011071300) {
$table = new xmldb_table('scorm');

$field = new xmldb_field('completionstatusrequired', XMLDB_TYPE_INTEGER, '1', XMLDB_UNSIGNED, null, null, null, 'timemodified');
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}

$field = new xmldb_field('completionscorerequired', XMLDB_TYPE_INTEGER, '2', XMLDB_UNSIGNED, null, null, null, 'completionstatusrequired');
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}

upgrade_mod_savepoint(true, 2011071300, 'scorm');
}

return true;
}

Expand Down
7 changes: 7 additions & 0 deletions mod/scorm/lang/en/scorm.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,13 @@
$string['browserepository'] = 'Browse repository';
$string['cannotfindsco'] = 'Could not find SCO';
$string['completed'] = 'Completed';
$string['completionscorerequired'] = 'Require minimum score';
$string['completionscorerequired_help'] = 'Enabling this setting will require a user to have at least the minimum score entered to be marked complete in this SCORM activity, as well as any other Activity Completion requirements.';
$string['completionstatus_failed'] = 'Failed';
$string['completionstatus_passed'] = 'Passed';
$string['completionstatus_completed'] = 'Completed';
$string['completionstatusrequired'] = 'Require status';
$string['completionstatusrequired_help'] = 'Checking one or more statuses will require a user to achieve at least one of the checked statuses in order to be marked complete in this SCORM activity, as well as any other Activity Completion requirements.';
$string['confirmloosetracks'] = 'WARNING: The package seems to be changed or modified. If the package structure is changed, some users tracks may be lost during update process.';
$string['contents'] = 'Contents';
$string['coursepacket'] = 'Course package';
Expand Down
138 changes: 138 additions & 0 deletions mod/scorm/lib.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,33 @@
define('SCORM_TYPE_IMSREPOSITORY', 'imsrepository');


/**
* Return an array of status options
*
* Optionally with translated strings
*
* @access public
* @param bool $with_strings (optional)
* @return array
*/
function scorm_status_options($with_strings = false) {
// Id's are important as they are bits
$options = array(
1 => 'failed',
2 => 'passed',
4 => 'completed'
);

if ($with_strings) {
foreach ($options as $key => $value) {
$options[$key] = get_string('completionstatus_'.$value, 'scorm');
}
}

return $options;
}


/**
* Given an object containing all the necessary data,
* (defined by the form in mod_form.php) this function
Expand Down Expand Up @@ -638,6 +665,18 @@ function scorm_grade_item_update($scorm, $grades=null) {
$grades = null;
}

// Update activity completion if applicable
// Get course info
$course = new object();
$course->id = $scorm->course;

$cm = get_coursemodule_from_instance('scorm', $scorm->id, $course->id);
// CM will be false if this has been run from scorm_add_instance
if ($cm) {
$completion = new completion_info($course);
$completion->update_state($cm, COMPLETION_COMPLETE);
}

return grade_update('mod/scorm', $scorm->course, 'mod', 'scorm', $scorm->id, 0, $grades, $params);
}

Expand Down Expand Up @@ -916,6 +955,7 @@ function scorm_pluginfile($course, $cm, $context, $filearea, $args, $forcedownlo
* @uses FEATURE_GROUPMEMBERSONLY
* @uses FEATURE_MOD_INTRO
* @uses FEATURE_COMPLETION_TRACKS_VIEWS
* @uses FEATURE_COMPLETION_HAS_RULES
* @uses FEATURE_GRADE_HAS_GRADE
* @uses FEATURE_GRADE_OUTCOMES
* @param string $feature FEATURE_xx constant for requested feature
Expand All @@ -928,6 +968,7 @@ function scorm_supports($feature) {
case FEATURE_GROUPMEMBERSONLY: return true;
case FEATURE_MOD_INTRO: return true;
case FEATURE_COMPLETION_TRACKS_VIEWS: return true;
case FEATURE_COMPLETION_HAS_RULES: return true;
case FEATURE_GRADE_HAS_GRADE: return true;
case FEATURE_GRADE_OUTCOMES: return true;
case FEATURE_BACKUP_MOODLE2: return true;
Expand Down Expand Up @@ -1080,3 +1121,100 @@ function scorm_page_type_list($pagetype, $parentcontext, $currentcontext) {
$module_pagetype = array('mod-scorm-*'=>get_string('page-mod-scorm-x', 'scorm'));
return $module_pagetype;
}

/**
* Obtains the automatic completion state for this scorm based on any conditions
* in scorm settings.
*
* @param object $course Course
* @param object $cm Course-module
* @param int $userid User ID
* @param bool $type Type of comparison (or/and; can be used as return value if no conditions)
* @return bool True if completed, false if not. (If no conditions, then return
* value depends on comparison type)
*/
function scorm_get_completion_state($course, $cm, $userid, $type) {
global $CFG, $DB;

$result = $type;

// Get scorm
if (!$scorm = $DB->get_record('scorm', array('id' => $cm->instance))) {
print_error('cannotfindscorm');
}

// Get user's tracks data
$tracks = $DB->get_records_sql(
"
SELECT
id,
element,
value
FROM
{scorm_scoes_track}
WHERE
scormid = ?
AND userid = ?
AND element IN
(
'cmi.core.lesson_status',
'cmi.completion_status',
'cmi.core.score.raw',
'cmi.score.raw'
)
",
array($scorm->id, $userid)
);

if (!$tracks) {
return completion_info::aggregate_completion_states($type, $result, false);
}

// Check for status
if ($scorm->completionstatusrequired !== null) {

// Get status
$statuses = array_flip(scorm_status_options());
$nstatus = 0;

foreach ($tracks as $track) {
if (!in_array($track->element, array('cmi.core.lesson_status', 'cmi.completion_status'))) {
continue;
}

if (array_key_exists($track->value, $statuses)) {
$nstatus |= $statuses[$track->value];
}
}

if ($scorm->completionstatusrequired & $nstatus) {
return completion_info::aggregate_completion_states($type, $result, true);
} else {
return completion_info::aggregate_completion_states($type, $result, false);
}

}

// Check for score
if ($scorm->completionscorerequired !== null) {
$maxscore = -1;

foreach ($tracks as $track) {
if (!in_array($track->element, array('cmi.core.score.raw', 'cmi.score.raw'))) {
continue;
}

if (strlen($track->value) && floatval($track->value) >= $maxscore) {
$maxscore = floatval($track->value);
}
}

if ($scorm->completionscorerequired <= $maxscore) {
return completion_info::aggregate_completion_states($type, $result, true);
} else {
return completion_info::aggregate_completion_states($type, $result, false);
}
}

return $result;
}
2 changes: 1 addition & 1 deletion mod/scorm/locallib.php
Original file line number Diff line number Diff line change
Expand Up @@ -415,7 +415,7 @@ function scorm_insert_track($userid, $scormid, $scoid, $attempt, $element, $valu
}

if (strstr($element, '.score.raw') ||
(($element == 'cmi.core.lesson_status' || $element == 'cmi.completion_status') && ($track->value == 'completed' || $track->value == 'passed'))) {
(($element == 'cmi.core.lesson_status' || $element == 'cmi.completion_status') && in_array($track->value, array('completed', 'passed', 'failed')))) {
$scorm = $DB->get_record('scorm', array('id' => $scormid));
include_once($CFG->dirroot.'/mod/scorm/lib.php');
scorm_update_grades($scorm, $userid);
Expand Down
92 changes: 92 additions & 0 deletions mod/scorm/mod_form.php
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,24 @@ function data_preprocessing(&$default_values) {
if (empty($default_values['timeclose'])) {
$default_values['timeclose'] = 0;
}

// Set some completion default data
if (!empty($default_values['completionstatusrequired']) && !is_array($default_values['completionstatusrequired'])) {
// Unpack values
$cvalues = array();
foreach (scorm_status_options() as $key => $value) {
if (($default_values['completionstatusrequired'] & $key) == $key) {
$cvalues[$key] = 1;
}
}

$default_values['completionstatusrequired'] = $cvalues;
}

if (!isset($default_values['completionscorerequired']) || !strlen($default_values['completionscorerequired'])) {
$default_values['completionscoredisabled'] = 1;
}

}

function validation($data, $files) {
Expand Down Expand Up @@ -409,4 +427,78 @@ function set_data($default_values) {
$this->data_preprocessing($default_values);
parent::set_data($default_values);
}

function add_completion_rules() {
$mform =& $this->_form;
$items = array();

// Require score
$group = array();
$group[] =& $mform->createElement('text', 'completionscorerequired', '', array('size' => 5));
$group[] =& $mform->createElement('checkbox', 'completionscoredisabled', null, get_string('disable'));
$mform->setType('completionscorerequired', PARAM_INT);
$mform->addGroup($group, 'completionscoregroup', get_string('completionscorerequired', 'scorm'), '', false);
$mform->addHelpButton('completionscoregroup', 'completionscorerequired', 'scorm');
$mform->disabledIf('completionscorerequired', 'completionscoredisabled', 'checked');
$mform->setDefault('completionscorerequired', 0);

$items[] = 'completionscoregroup';


// Require status
$first = true;
$firstkey = null;
foreach (scorm_status_options(true) as $key => $value) {
$name = null;
$key = 'completionstatusrequired['.$key.']';
if ($first) {
$name = get_string('completionstatusrequired', 'scorm');
$first = false;
$firstkey = $key;
}
$mform->addElement('checkbox', $key, $name, $value);
$mform->setType($key, PARAM_BOOL);
$items[] = $key;
}
$mform->addHelpButton($firstkey, 'completionstatusrequired', 'scorm');

return $items;
}

function completion_rule_enabled($data) {
$status = !empty($data['completionstatusrequired']);
$score = empty($data['completionscoredisabled']) && strlen($data['completionscorerequired']);

return $status || $score;
}

function get_data($slashed = true) {
$data = parent::get_data($slashed);

if (!$data) {
return false;
}

// Turn off completion settings if the checkboxes aren't ticked
$autocompletion = !empty($data->completion) && $data->completion == COMPLETION_TRACKING_AUTOMATIC;

if (isset($data->completionstatusrequired) && is_array($data->completionstatusrequired)) {
$total = 0;
foreach (array_keys($data->completionstatusrequired) as $state) {
$total |= $state;
}

$data->completionstatusrequired = $total;
}

if (!$autocompletion) {
$data->completionstatusrequired = null;
}

if (!empty($data->completionscoredisabled) || !$autocompletion) {
$data->completionscorerequired = null;
}

return $data;
}
}
3 changes: 1 addition & 2 deletions mod/scorm/version.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/

$module->version = 2011021402; // The (date) version of this module
$module->version = 2011071300; // 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)?

0 comments on commit 45fb80c

Please sign in to comment.