Skip to content

Commit

Permalink
Learning path: Add option to use score as progress in single-SCO SCOR…
Browse files Browse the repository at this point in the history
…M packages

Implement feature request #3069
  • Loading branch information
ywarnier committed Feb 9, 2020
1 parent 4f090d4 commit 5643bea
Show file tree
Hide file tree
Showing 8 changed files with 177 additions and 52 deletions.
4 changes: 4 additions & 0 deletions main/install/configuration.dist.php
Original file line number Diff line number Diff line change
Expand Up @@ -1378,6 +1378,10 @@
// Catalog search settings visibility
//$_configuration['catalog_settings'] = ['sessions' => ['by_title' => true, 'by_date' => true, 'by_tag' => true, 'show_session_info' => true, 'show_session_date' => true]];

// Enable learning paths with only one SCO item to use the score returned by
// the SCO as an indicator of progress of the whole learning path
// $_configuration['lp_score_as_progress_enable'] = false;

// Use this link as the "Forgot password?" link instead of the default. This setting should be transformed into a hook for plugins at a later time
//$_configuration['pass_reminder_custom_link'] = '';

Expand Down
13 changes: 7 additions & 6 deletions main/lang/english/trad4all.inc.php
Original file line number Diff line number Diff line change
Expand Up @@ -751,7 +751,7 @@
$ArchivesDirectoryNotWriteableContactAdmin = "The app/cache/ directory, used by this tool, is not writeable. Please contact your platform administrator.";
$DestinationCourse = "Target course";
$ConvertToMultipleAnswer = "Convert to multiple answer";
$CasMainActivateComment = "Enabling CAS authentication will allow users to authenticate with their CAS credentials.<br/>Go to <a href='settings.php?category=CAS'>Plugin</a> to add a configurable 'CAS Login' button for your Chamilo campus. Or you can force CAS authentication by setting cas[force_redirect] in app/config/auth.conf.php.";
$CasMainActivateComment = "Enabling CAS authentication will allow users to authenticate with their CAS credentials.<br/>Go to <a href='settings.php?category=CAS'>Plugin</a> to add a configurable 'CAS Login' button for your Chamilo campus.";
$UsersRegisteredInAnyGroup = "Users registered in any group";
$Camera = "Camera";
$Microphone = "Microphone";
Expand Down Expand Up @@ -6818,7 +6818,6 @@
$CasMainProtocolTitle = "Main CAS server protocol";
$CAS1Text = "CAS 1";
$CAS2Text = "CAS 2";
$CAS3Text = "CAS 3";
$SAMLText = "SAML";
$CasMainProtocolComment = "The protocol with which we connect to the CAS server";
$CasUserAddActivateTitle = "Enable CAS user addition";
Expand Down Expand Up @@ -7162,10 +7161,10 @@
$SessionadminAutosubscribeComment = "Session administrator autosubscribe - not available yet";
$ToolVisibleByDefaultAtCreationTitle = "Tool visible at course creation";
$ToolVisibleByDefaultAtCreationComment = "Select the tools that will be visible when creating the courses - not yet available";
$casAddUserActivatePlatform = "Create a user account for any new CAS-authenticated user, from scratch";
$casAddUserActivateLDAP = "Create a user account for any new CAS-authenticated user, from LDAP";
$UpdateUserInfoCasWithLdapTitle = "Update CAS-authenticated user account information from LDAP";
$UpdateUserInfoCasWithLdapComment = "Makes sure the user firstname, lastname and email address are the same as current values in the LDAP directory";
$casAddUserActivatePlatform = "CAS internal setting";
$casAddUserActivateLDAP = "CAS internal setting";
$UpdateUserInfoCasWithLdapTitle = "CAS internal setting";
$UpdateUserInfoCasWithLdapComment = "CAS internal setting";
$InstallExecution = "Installation process execution";
$UpdateExecution = "Update process execution";
$PleaseWaitThisCouldTakeAWhile = "Please wait. This could take a while...";
Expand Down Expand Up @@ -8449,4 +8448,6 @@
$UserClassExplanation = "Information: The list of classes below contains the list of classes you have already registered in your course. If this list is empty, use the + green above to add classes.";
$InsertTwoNames = "Insert your two names";
$AddRightLogo = "Add right logo";
$LearnpathUseScoreAsProgress = "Use score as progress";
$LearnpathUseScoreAsProgressComment = "Use the score returned, by the only SCO in this learning path, as the progress indicator in the progress bar. This modifies the SCORM behaviour in the strict sense, but improves visual feedback to the learner.";
?>
136 changes: 98 additions & 38 deletions main/lp/learnpath.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -1965,9 +1965,9 @@ public function get_next_item_id()
* @param string $file_path the path to the file
* @param string $file_name the original name of the file
*
* @return string 'scorm','aicc','scorm2004','dokeos' or '' if the package cannot be recognized
* @return string 'scorm','aicc','scorm2004','dokeos', 'error-empty-package' if the package is empty, or '' if the package cannot be recognized
*/
public static function get_package_type($file_path, $file_name)
public static function getPackageType($file_path, $file_name)
{
// Get name of the zip file without the extension.
$file_info = pathinfo($file_name);
Expand All @@ -1994,43 +1994,47 @@ public static function get_package_type($file_path, $file_name)
$aicc_match_au = 0;
$aicc_match_des = 0;
$aicc_match_cst = 0;
$countItems = 0;

// The following loop should be stopped as soon as we found the right imsmanifest.xml (how to recognize it?).
if (is_array($zipContentArray) && count($zipContentArray) > 0) {
foreach ($zipContentArray as $thisContent) {
if (preg_match('~.(php.*|phtml)$~i', $thisContent['filename'])) {
// New behaviour: Don't do anything. These files will be removed in scorm::import_package.
} elseif (stristr($thisContent['filename'], 'imsmanifest.xml') !== false) {
$manifest = $thisContent['filename']; // Just the relative directory inside scorm/
$package_type = 'scorm';
break; // Exit the foreach loop.
} elseif (
preg_match('/aicc\//i', $thisContent['filename']) ||
in_array(
strtolower(pathinfo($thisContent['filename'], PATHINFO_EXTENSION)),
['crs', 'au', 'des', 'cst']
)
) {
$ext = strtolower(pathinfo($thisContent['filename'], PATHINFO_EXTENSION));
switch ($ext) {
case 'crs':
$aicc_match_crs = 1;
break;
case 'au':
$aicc_match_au = 1;
break;
case 'des':
$aicc_match_des = 1;
break;
case 'cst':
$aicc_match_cst = 1;
break;
default:
break;
if (is_array($zipContentArray)) {
$countItems = count($zipContentArray);
if ($countItems > 0) {
foreach ($zipContentArray as $thisContent) {
if (preg_match('~.(php.*|phtml)$~i', $thisContent['filename'])) {
// New behaviour: Don't do anything. These files will be removed in scorm::import_package.
} elseif (stristr($thisContent['filename'], 'imsmanifest.xml') !== false) {
$manifest = $thisContent['filename']; // Just the relative directory inside scorm/
$package_type = 'scorm';
break; // Exit the foreach loop.
} elseif (
preg_match('/aicc\//i', $thisContent['filename']) ||
in_array(
strtolower(pathinfo($thisContent['filename'], PATHINFO_EXTENSION)),
['crs', 'au', 'des', 'cst']
)
) {
$ext = strtolower(pathinfo($thisContent['filename'], PATHINFO_EXTENSION));
switch ($ext) {
case 'crs':
$aicc_match_crs = 1;
break;
case 'au':
$aicc_match_au = 1;
break;
case 'des':
$aicc_match_des = 1;
break;
case 'cst':
$aicc_match_cst = 1;
break;
default:
break;
}
//break; // Don't exit the loop, because if we find an imsmanifest afterwards, we want it, not the AICC.
} else {
$package_type = '';
}
//break; // Don't exit the loop, because if we find an imsmanifest afterwards, we want it, not the AICC.
} else {
$package_type = '';
}
}
}
Expand All @@ -2042,6 +2046,13 @@ public static function get_package_type($file_path, $file_name)

// Try with chamilo course builder
if (empty($package_type)) {
// Sometimes users will try to upload an empty zip, or a zip with
// only a folder. Catch that and make the calling function aware.
// If the single file was the imsmanifest.xml, then $package_type
// would be 'scorm' and we wouldn't be here.
if ($countItems < 2) {
return 'error-empty-package';
}
$package_type = 'chamilo';
}

Expand Down Expand Up @@ -2538,16 +2549,40 @@ public function get_progress_bar_text($mode = '', $add = 0)
if (empty($mode)) {
$mode = $this->progress_bar_mode;
}
$text = '';
$percentage = 0;
// If the option to use the score as progress is set for this learning
// path, then the rules are completely different: we assume only one
// item exists and the progress of the LP depends on the score
$scoreAsProgressSetting = api_get_configuration_value('lp_score_as_progress_enable');
if ($scoreAsProgressSetting === true) {
$scoreAsProgress = $this->getUseScoreAsProgress();
if ($scoreAsProgress) {
// Get single item's score
$itemId = $this->get_current_item_id();
$item = $this->getItem($itemId);
$score = $item->get_score();
$maxScore = $item->get_max();
if ($mode = '%') {
$percentage = ((float) $score / (float) $maxScore) * 100;
$percentage = number_format($percentage, 0);
$text = '%';
} else {
$percentage = $score;
$text = '/'.$maxScore;
}
return [$percentage, $text];
}
}
// otherwise just continue the normal processing of progress
$total_items = $this->getTotalItemsCountWithoutDirs();
$completeItems = $this->get_complete_items_count();
if ($add != 0) {
$completeItems += $add;
}
$text = '';
if ($completeItems > $total_items) {
$completeItems = $total_items;
}
$percentage = 0;
if ($mode == '%') {
if ($total_items > 0) {
$percentage = ((float) $completeItems / (float) $total_items) * 100;
Expand Down Expand Up @@ -13740,4 +13775,29 @@ private function getSavedFinalItem()

return '';
}
/**
* Gets whether this SCORM learning path has been marked to use the score
* as progress. Takes into account whether the learnpath matches (SCORM
* content + less than 2 items).
* @return bool True if the score should be used as progress, false otherwise
*/
public function getUseScoreAsProgress()
{
// If not a SCORM, we don't care about the setting
if ($this->get_type() != 2) {
return false;
}
// If more than one step in the SCORM, we don't care about the setting
if ($this->get_total_items_count() > 1) {
return false;
}
$extraFieldValue = new ExtraFieldValue('lp');
$doUseScore = false;
$useScore = $extraFieldValue->get_values_by_handler_and_field_variable($this->get_id(), 'use_score_as_progress');
if (!empty($useScore) && isset($useScore['value'])) {
$doUseScore = $useScore['value'];
}

return $doUseScore;
}
}
15 changes: 14 additions & 1 deletion main/lp/lp_ajax_save_item.php
Original file line number Diff line number Diff line change
Expand Up @@ -474,7 +474,20 @@ function save_item(
$return .= "update_toc('".$my_upd_status."','".$my_upd_id."');";
}
}
$return .= "update_progress_bar('$myComplete', '$myTotal', '$myProgressMode');";
$progressBarSpecial = false;
$scoreAsProgressSetting = api_get_configuration_value('lp_score_as_progress_enable');
if ($scoreAsProgressSetting === true) {
$scoreAsProgress = $myLP->getUseScoreAsProgress();
if ($scoreAsProgress) {
$score = $myLPI->get_score();
$maxScore = $myLPI->get_max();
$return .= "update_progress_bar('$score', '$maxScore', '$myProgressMode');";
$progressBarSpecial = true;
}
}
if (!$progressBarSpecial) {
$return .= "update_progress_bar('$myComplete', '$myTotal', '$myProgressMode');";
}

if (!Session::read('login_as')) {
// If $_SESSION['login_as'] is set, then the user is an admin logged as the user.
Expand Down
20 changes: 18 additions & 2 deletions main/lp/lp_ajax_switch_item_toc.php
Original file line number Diff line number Diff line change
Expand Up @@ -151,8 +151,24 @@ function switch_item_toc($lpId, $userId, $viewId, $currentItem, $nextItem)

$return .= "update_toc('unhighlight','".$currentItem."');".
"update_toc('highlight','".$newItemId."');".
"update_toc('$lessonStatus','".$newItemId."');".
"update_progress_bar('$completedItems','$totalItems','$progressMode');";
"update_toc('$lessonStatus','".$newItemId."');";

$progressBarSpecial = false;
$scoreAsProgressSetting = api_get_configuration_value('lp_score_as_progress_enable');
if ($scoreAsProgressSetting === true) {
$scoreAsProgress = $myLP->getUseScoreAsProgress();
if ($scoreAsProgress) {
$score = $myLPI->get_score();
$maxScore = $myLPI->get_max();
$return .= "update_progress_bar('$score', '$maxScore', '$progressMode');";
$progressBarSpecial = true;
}
}
if (!$progressBarSpecial) {
$return .= "update_progress_bar('$completedItems','$totalItems','$progressMode');";
}



$myLP->set_error_msg('');
$myLP->prerequisites_match(); // Check the prerequisites are all complete.
Expand Down
25 changes: 24 additions & 1 deletion main/lp/lp_edit.php
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,24 @@ function activate_end_date() {
get_lang('AccumulateScormTime')
);

$scoreAsProgressSetting = api_get_configuration_value('lp_score_as_progress_enable');
$countItems = $learnPath->get_total_items_count();
$lpType = $learnPath->get_type();
// This option is only usable for SCORM, if there is only 1 item, otherwise
// using the score as progress would not work anymore (we would have to divide
// between the two without knowing if the second has any score at all)
// TODO: automatically cancel this setting if items >= 2
if ($scoreAsProgressSetting && $countItems < 2 && $lpType == 2) {
$scoreAsProgress = $learnPath->getUseScoreAsProgress();
$form->addElement(
'checkbox',
'extra_use_score_as_progress',
[null, get_lang('LearnpathUseScoreAsProgressComment')],
get_lang('LearnpathUseScoreAsProgress')
);
$defaults['extra_use_score_as_progress'] = $scoreAsProgress;
}

$options = learnpath::getIconSelect();

if (!empty($options)) {
Expand All @@ -247,10 +265,15 @@ function activate_end_date() {
}

$extraField = new ExtraField('lp');
$extra = $extraField->addElements($form, $lpId, ['lp_icon']);
$extra = $extraField->addElements(
$form,
$lpId,
['lp_icon', 'use_score_as_progress']
);

$skillList = Skill::addSkillsToForm($form, ITEM_TYPE_LEARNPATH, $lpId);


// Submit button
$form->addButtonSave(get_lang('SaveLPSettings'));

Expand Down
4 changes: 2 additions & 2 deletions main/lp/lp_upload.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
$file_base_name = str_replace('.'.$extension, '', $filename);

$new_dir = api_replace_dangerous_char(trim($file_base_name));
$type = learnpath::get_package_type(
$type = learnpath::getPackageType(
$_FILES['user_file']['tmp_name'],
$_FILES['user_file']['name']
);
Expand Down Expand Up @@ -162,7 +162,7 @@
Display::return_message(get_lang('UplFileTooBig'))
);
}
$type = learnpath::get_package_type($s, basename($s));
$type = learnpath::getPackageType($s, basename($s));

switch ($type) {
case 'scorm':
Expand Down
12 changes: 10 additions & 2 deletions main/lp/scorm_api.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@
'lp',
Session::read('lpobject')
);
if (!is_object($oLP)) {
error_log('New LP - scorm_api - Could not load oLP object', 0);
exit;
}
/** @var learnpathItem $oItem */
$oItem = isset($oLP->items[$oLP->current]) ? $oLP->items[$oLP->current] : null;

Expand All @@ -50,6 +54,7 @@

?>var scorm_logs=<?php echo (empty($oLP->scorm_debug) or (!api_is_course_admin() && !api_is_platform_admin())) ? '0' : '3'; ?>; //debug log level for SCORM. 0 = none, 1=light, 2=a lot, 3=all - displays logs in log frame
var lms_logs = 0; //debug log level for LMS actions. 0=none, 1=light, 2=a lot, 3=all
var score_as_progress = <?php echo (empty($oLP->getUseScoreAsProgress())? 'false':'true'); ?>;

// API Object initialization (eases access later on)
function APIobject() {
Expand Down Expand Up @@ -650,6 +655,9 @@ function LMSSetValue(param, val) {
if (param == "cmi.core.score.raw") {
olms.score= val;
olms.updatable_vars_list['cmi.core.score.raw']=true;
if (score_as_progress) {
update_progress_bar(val, olms.max, '%');
}
return_value='true';
} else if ( param == "cmi.core.score.max") {
olms.max = val;
Expand Down Expand Up @@ -2410,9 +2418,9 @@ function attach_glossary_into_scorm(type) {
*/
function update_time_bar(nbr_complete, nbr_total, mode)
{
logit_lms('update_progress_bar('+nbr_complete+', '+nbr_total+', '+mode+')',3);
logit_lms('update_time_bar('+nbr_complete+', '+nbr_total+', '+mode+')',3);
logit_lms(
'update_progress_bar with params: lms_lp_id= ' + olms.lms_lp_id +
'update_time_bar with params: lms_lp_id= ' + olms.lms_lp_id +
', lms_view_id= '+ olms.lms_view_id + ' lms_user_id= '+ olms.lms_user_id,
3
);
Expand Down

0 comments on commit 5643bea

Please sign in to comment.