Partial merge with 1.11.x see BT#15952

pull/3063/head
Julio Montoya 6 years ago
parent 9ff9f0fa6c
commit dbd33061c1
  1. 4
      .gitignore
  2. 5
      .htaccess
  3. 1
      .php_cs.dist
  4. 2
      .travis.yml
  5. 29
      certificates/index.php
  6. 19
      main/document/showinframes.php
  7. 34
      main/exercise/TestCategory.php
  8. 165
      main/exercise/overview.php
  9. 10
      main/inc/lib/document.lib.php
  10. 2
      main/inc/lib/extra_field.lib.php
  11. 4
      main/inc/lib/javascript/ViewerJS/index.html
  12. 342
      main/inc/lib/myspace.lib.php
  13. 1
      main/inc/lib/pear/HTML/QuickForm/select.php
  14. 1
      main/inc/lib/sessionmanager.lib.php
  15. 119
      main/lang/english/trad4all.inc.php
  16. 118
      main/lang/french/trad4all.inc.php
  17. 18
      main/mySpace/myStudents.php
  18. 2
      main/template/default/exercise/submit.js.tpl
  19. 144
      main/template/default/learnpath/view.tpl
  20. 278
      plugin/questionoptionsevaluation/QuestionOptionsEvaluationPlugin.php
  21. 20
      plugin/questionoptionsevaluation/README.md
  22. 74
      plugin/questionoptionsevaluation/evaluation.php
  23. 4
      plugin/questionoptionsevaluation/install.php
  24. 19
      plugin/questionoptionsevaluation/lang/english.php
  25. 19
      plugin/questionoptionsevaluation/lang/spanish.php
  26. 4
      plugin/questionoptionsevaluation/plugin.php
  27. 4
      plugin/questionoptionsevaluation/uninstall.php
  28. 42
      plugin/surveyexportcsv/README.md
  29. 95
      plugin/surveyexportcsv/SurveyExportCsvPlugin.php
  30. 215
      plugin/surveyexportcsv/export.php
  31. 4
      plugin/surveyexportcsv/install.php
  32. 9
      plugin/surveyexportcsv/lang/english.php
  33. 9
      plugin/surveyexportcsv/lang/spanish.php
  34. 4
      plugin/surveyexportcsv/plugin.php
  35. 50
      plugin/surveyexportcsv/start.php
  36. 4
      plugin/surveyexportcsv/uninstall.php
  37. 10
      plugin/whispeakauth/README.md
  38. 293
      plugin/whispeakauth/WhispeakAuthPlugin.php
  39. 136
      plugin/whispeakauth/ajax/record_audio.php
  40. 139
      plugin/whispeakauth/assets/js/RecordAudio.js
  41. 26
      plugin/whispeakauth/authentify.php
  42. 28
      plugin/whispeakauth/enrollment.php
  43. 14
      plugin/whispeakauth/index.php
  44. 4
      plugin/whispeakauth/install.php
  45. 33
      plugin/whispeakauth/lang/english.php
  46. 18
      plugin/whispeakauth/lang/french.php
  47. 33
      plugin/whispeakauth/lang/spanish.php
  48. 4
      plugin/whispeakauth/plugin.php
  49. 4
      plugin/whispeakauth/uninstall.php
  50. 38
      plugin/whispeakauth/view/authentify_recorder.html.twig
  51. 67
      plugin/whispeakauth/view/record_audio.html.twig
  52. 108
      tests/behat/features/bootstrap/FeatureContext.php
  53. 16
      tests/behat/features/course_user_registration.feature
  54. 5
      tests/behat/features/sessionManagement.feature
  55. 10
      tests/behat/features/toolAnnouncement.feature
  56. 17
      tests/behat/features/toolDocument.feature
  57. 86
      tests/behat/features/toolExercise.feature
  58. 318
      tests/behat/features/toolGroup.feature
  59. 20
      tests/behat/features/toolLp.feature

4
.gitignore vendored

@ -18,6 +18,7 @@ app/logs/*
# Courses # Courses
app/courses/* app/courses/*
!app/courses/proxy.php
# Home # Home
app/home/* app/home/*
@ -49,4 +50,5 @@ plugin/vchamilo/templates/*
# Stuff updated through composer - Remove just before release # Stuff updated through composer - Remove just before release
vendor vendor
web/assets/* web/assets/*
node_modules
yarn.lock

@ -9,7 +9,7 @@
RewriteEngine on RewriteEngine on
# Prevent execution of PHP from directories used for different types of uploads # Prevent execution of PHP from directories used for different types of uploads
RedirectMatch 403 ^/app/(cache|courses|home|logs|upload|Resources/public/css)/.*\.ph(p[3457]?|t|tml|ar)$ RedirectMatch 403 ^/app/(?!courses/proxy)(cache|courses|home|logs|upload|Resources/public/css)/.*\.ph(p[3457]?|t|tml|ar)$
RedirectMatch 403 ^/main/default_course_document/images/.*\.ph(p[3457]?|t|tml|ar)$ RedirectMatch 403 ^/main/default_course_document/images/.*\.ph(p[3457]?|t|tml|ar)$
RedirectMatch 403 ^/main/lang/.*\.ph(p[3457]?|t|tml|ar)$ RedirectMatch 403 ^/main/lang/.*\.ph(p[3457]?|t|tml|ar)$
RedirectMatch 403 ^/web/css/.*\.ph(p[3457]?|t|tml|ar)$ RedirectMatch 403 ^/web/css/.*\.ph(p[3457]?|t|tml|ar)$
@ -75,6 +75,9 @@ RewriteRule ^lti/os$ plugin/ims_lti/outcome_service.php [L]
# http://my.chamilo.net/jdoe to http://my.chamilo.net/user.php?jdoe # http://my.chamilo.net/jdoe to http://my.chamilo.net/user.php?jdoe
RewriteRule ^([^/.]+)/?$ user.php?$1 [L] RewriteRule ^([^/.]+)/?$ user.php?$1 [L]
# Deny direct access to user my files
RewriteRule ^app/upload/users/([^/]+)/([^/]+)/my_files/(.*)$ main/social/download_my_files.php?user_id=$2&file=$3 [QSA,L]
# Add caching of woff font files to avoid loading 2*15KB each time with Chamilo # Add caching of woff font files to avoid loading 2*15KB each time with Chamilo
# default OpenSans font # default OpenSans font

@ -58,7 +58,6 @@ $finder = PhpCsFixer\Finder::create()
->exclude('main/inc/lib/ppt2png') ->exclude('main/inc/lib/ppt2png')
->exclude('main/inc/lib/phpseclib') ->exclude('main/inc/lib/phpseclib')
->exclude('main/inc/lib/pear') ->exclude('main/inc/lib/pear')
->exclude('main/inc/lib/phpseclib')
->exclude('main/inc/lib/svg-edit') ->exclude('main/inc/lib/svg-edit')
->exclude('main/inc/lib/swfobject') ->exclude('main/inc/lib/swfobject')
->exclude('main/inc/lib/wami-recorder') ->exclude('main/inc/lib/wami-recorder')

@ -18,11 +18,11 @@ cache:
directories: directories:
- $HOME/.composer/cache/files - $HOME/.composer/cache/files
php: php:
- 5.5
- 5.6 - 5.6
- 7.0 - 7.0
- 7.1 - 7.1
- 7.2 - 7.2
- 7.3
env: env:
global: global:

@ -1,5 +1,6 @@
<?php <?php
/* For licensing terms, see /license.txt */ /* For licensing terms, see /license.txt */
/** /**
* Show specified user certificate. * Show specified user certificate.
* *
@ -8,9 +9,33 @@
require_once '../main/inc/global.inc.php'; require_once '../main/inc/global.inc.php';
$action = isset($_GET['action']) ? $_GET['action'] : null; $action = isset($_GET['action']) ? $_GET['action'] : null;
$certificate = new Certificate($_GET['id']); $userId = isset($_GET['user_id']) ? $_GET['user_id'] : 0;
$certificateId = isset($_GET['id']) ? $_GET['id'] : 0;
$certificate = new Certificate($certificateId, $userId);
$certificateData = $certificate->get($certificateId);
if (empty($certificateData)) {
api_not_allowed(false, Display::return_message(get_lang('NoCertificateAvailable'), 'warning'));
}
$category = Category::findByCertificate($certificateId);
// Check if the certificate should use the course language
if (!empty($category) && !empty($category->get_course_code())) {
$courseInfo = api_get_course_info($category->get_course_code());
$language = $courseInfo['language'];
$languageFilesToLoad = api_get_language_files_to_load($language);
foreach ($languageFilesToLoad as $languageFile) {
include $languageFile;
}
// Overwrite the interface language with the course language
$language_interface = $language;
$language_interface_initial_value = $language_interface;
}
CustomCertificatePlugin::redirectCheck($certificate, $_GET['id']); CustomCertificatePlugin::redirectCheck($certificate, $certificateId, $userId);
switch ($action) { switch ($action) {
case 'export': case 'export':

@ -174,7 +174,7 @@ if (api_is_course_admin()) {
$frameheight = 165; $frameheight = 165;
} }
$frameReady = Display::getFrameReadyBlock('top.mainFrame'); $frameReady = Display::getFrameReadyBlock('#mainFrame');
$web_odf_supported_files = DocumentManager::get_web_odf_extension_list(); $web_odf_supported_files = DocumentManager::get_web_odf_extension_list();
// PDF should be displayed with viewerJS // PDF should be displayed with viewerJS
@ -237,19 +237,14 @@ if (!$playerSupported && $execute_iframe) {
</script>'; </script>';
$htmlHeadXtra[] = '<script type="text/javascript" src="'.api_get_path(WEB_LIBRARY_PATH).'javascript/jquery.frameready.js"></script>'; $htmlHeadXtra[] = '<script type="text/javascript" src="'.api_get_path(WEB_LIBRARY_PATH).'javascript/jquery.frameready.js"></script>';
$htmlHeadXtra[] = '<script> $htmlHeadXtra[] = '<script>
var updateContentHeight = function() {
my_iframe = document.getElementById("mainFrame");
if (my_iframe) {
//this doesnt seem to work in IE 7,8,9
my_iframe.height = my_iframe.contentWindow.document.body.scrollHeight + 50 + "px";
}
};
// Fixes the content height of the frame // Fixes the content height of the frame
window.onload = function() { $(function() {
updateContentHeight(); $(\'#mainFrame\').on(\'load\', function () {
this.style.height = (this.contentWindow.document.body.scrollHeight + 50) + \'px\';
});
'.$frameReady.' '.$frameReady.'
} });
</script>'; </script>';
} }

@ -714,40 +714,6 @@ class TestCategory
return $tabResult; return $tabResult;
} }
/**
* return total score for test exe_id for all question in the category $in_cat_id for user
* If no question for this category, return "".
*/
public static function getCatScoreForExeidForUserid($in_cat_id, $in_exe_id, $in_user_id)
{
$tbl_track_attempt = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
$tbl_question_rel_category = Database::get_course_table(TABLE_QUIZ_QUESTION_REL_CATEGORY);
$in_cat_id = (int) $in_cat_id;
$in_exe_id = (int) $in_exe_id;
$in_user_id = (int) $in_user_id;
$query = "SELECT DISTINCT
marks,
exe_id,
user_id,
ta.question_id,
category_id
FROM $tbl_track_attempt ta
INNER JOIN $tbl_question_rel_category qrc
ON (ta.question_id = qrc.question_id)
WHERE
qrc.category_id = $in_cat_id AND
exe_id = $in_exe_id AND
user_id = $in_user_id";
$res = Database::query($query);
$score = '';
while ($data = Database::fetch_array($res)) {
$score += $data['marks'];
}
return $score;
}
/** /**
* Return the number max of question in a category * Return the number max of question in a category
* count the number of questions in all categories, and return the max. * count the number of questions in all categories, and return the max.

@ -77,38 +77,51 @@ if ($time_control) {
} }
if (!in_array($origin, ['learnpath', 'embeddable'])) { if (!in_array($origin, ['learnpath', 'embeddable'])) {
$fluid = false; Display::display_header();
} else { } else {
$htmlHeadXtra[] = " $htmlHeadXtra[] = "
<style> <style>
body { background: none;} body { background: none;}
</style> </style>
"; ";
$fluid = true; Display::display_reduced_header();
} }
$tpl = new Template('Overview');
$list = [];
$html = ''; $html = '';
$message = ''; $message = '';
$html .= '<div class="exercise-overview">';
$is_allowed_to_edit = api_is_allowed_to_edit(null, true);
$editLink = '';
if ($is_allowed_to_edit) {
if ($objExercise->sessionId == $sessionId) {
$editLink = Display::url(
Display::return_icon('edit.png', get_lang('Edit'), [], ICON_SIZE_SMALL),
api_get_path(WEB_CODE_PATH).'exercise/admin.php?'.api_get_cidreq().'&exerciseId='.$objExercise->id
);
}
$editLink .= Display::url(
Display::return_icon('test_results.png', get_lang('Results'), [], ICON_SIZE_SMALL),
api_get_path(WEB_CODE_PATH).'exercise/exercise_report.php?'.api_get_cidreq().'&exerciseId='.$objExercise->id,
['title' => get_lang('Results')]
);
}
$isAllowedToEdit = api_is_allowed_to_edit(null, true); $iconExercise = Display::return_icon('test-quiz.png', null, [], ICON_SIZE_MEDIUM);
$list['id'] = $objExercise->id;
$list['session_id'] = $objExercise->sessionId;
$list['edit'] = $isAllowedToEdit;
// Exercise name. // Exercise name.
if (api_get_configuration_value('save_titles_as_html')) { if (api_get_configuration_value('save_titles_as_html')) {
$list['title'] = $objExercise->get_formated_title().PHP_EOL; $html .= Display::div(
$objExercise->get_formated_title().PHP_EOL.$editLink
);
} else { } else {
$list['title'] = $objExercise->selectTitle().PHP_EOL; $html .= Display::page_header(
$iconExercise.PHP_EOL.$objExercise->selectTitle().PHP_EOL.$editLink
);
} }
//Exercise description // Exercise description
if (!empty($objExercise->description)) { if (!empty($objExercise->description)) {
$list['description'] = $objExercise->description; $html .= Display::div($objExercise->description, ['class' => 'exercise_description']);
} }
$extra_params = ''; $extra_params = '';
@ -116,7 +129,7 @@ if (isset($_GET['preview'])) {
$extra_params = '&preview=1'; $extra_params = '&preview=1';
} }
$exerciseStatInfo = $objExercise->get_stat_track_exercise_info( $exercise_stat_info = $objExercise->get_stat_track_exercise_info(
$learnpath_id, $learnpath_id,
$learnpath_item_id, $learnpath_item_id,
0 0
@ -124,33 +137,38 @@ $exerciseStatInfo = $objExercise->get_stat_track_exercise_info(
//1. Check if this is a new attempt or a previous //1. Check if this is a new attempt or a previous
$label = get_lang('StartTest'); $label = get_lang('StartTest');
if ($time_control && !empty($clock_expired_time) || isset($exerciseStatInfo['exe_id'])) { if ($time_control && !empty($clock_expired_time) || isset($exercise_stat_info['exe_id'])) {
$label = get_lang('ContinueTest'); $label = get_lang('ContinueTest');
} }
$msgStatus = null; if (isset($exercise_stat_info['exe_id'])) {
if (isset($exerciseStatInfo['exe_id'])) { $message = Display::return_message(get_lang('YouTriedToResolveThisExerciseEarlier'));
$msgStatus = get_lang('YouTriedToResolveThisExerciseEarlier');
} }
// 2. Exercise button // 2. Exercise button
// Notice we not add there the lp_item_view_id because is not already generated // Notice we not add there the lp_item_view_id because is not already generated
$list['url'] = api_get_path(WEB_CODE_PATH).'exercise/exercise_submit.php?'.api_get_cidreq().'&exerciseId='.$objExercise->id.'&learnpath_id='.$learnpath_id.'&learnpath_item_id='.$learnpath_item_id.'&learnpath_item_view_id='.$learnpathItemViewId.$extra_params; $exercise_url = api_get_path(WEB_CODE_PATH).'exercise/exercise_submit.php?'.api_get_cidreq().'&exerciseId='.$objExercise->id.'&learnpath_id='.$learnpath_id.'&learnpath_item_id='.$learnpath_item_id.'&learnpath_item_view_id='.$learnpathItemViewId.$extra_params;
$exercise_url_button = Display::url(
$label,
$exercise_url,
['class' => 'btn btn-success btn-large']
);
//3. Checking visibility of the exercise (overwrites the exercise button) //3. Checking visibility of the exercise (overwrites the exercise button)
$list['is_visible'] = $objExercise->is_visible( $visible_return = $objExercise->is_visible(
$learnpath_id, $learnpath_id,
$learnpath_item_id, $learnpath_item_id,
null, null,
false true
); );
// Exercise is not visible remove the button // Exercise is not visible remove the button
if ($list['is_visible']['value'] == false) { if ($visible_return['value'] == false) {
if ($isAllowedToEdit) { if ($is_allowed_to_edit) {
$message = get_lang('ThisItemIsInvisibleForStudentsButYouHaveAccessAsTeacher'); $message = Display::return_message(get_lang('ThisItemIsInvisibleForStudentsButYouHaveAccessAsTeacher'), 'warning');
} else { } else {
$message = $list['is_visible']['message']; $message = $visible_return['message'];
$exercise_url_button = null;
} }
} }
@ -166,7 +184,7 @@ $attempts = Event::getExerciseResultsByUser(
$counter = count($attempts); $counter = count($attempts);
$my_attempt_array = []; $my_attempt_array = [];
$tableContent = ''; $table_content = '';
/* Make a special case for IE, which doesn't seem to be able to handle the /* Make a special case for IE, which doesn't seem to be able to handle the
* results popup -> send it to the full results page */ * results popup -> send it to the full results page */
@ -193,15 +211,26 @@ if (in_array(
} }
} }
$certificateBlock = '';
if (!empty($attempts)) { if (!empty($attempts)) {
$i = $counter; $i = $counter;
foreach ($attempts as $attempt_result) { foreach ($attempts as $attempt_result) {
if (empty($certificateBlock)) {
$certificateBlock = ExerciseLib::generateAndShowCertificateBlock(
$attempt_result['exe_result'],
$attempt_result['exe_weighting'],
$objExercise,
$attempt_result['exe_user_id'],
$courseCode,
$sessionId
);
}
$score = ExerciseLib::show_score($attempt_result['exe_result'], $attempt_result['exe_weighting']); $score = ExerciseLib::show_score($attempt_result['exe_result'], $attempt_result['exe_weighting']);
$attempt_url = api_get_path(WEB_CODE_PATH).'exercise/result.php?'; $attempt_url = api_get_path(WEB_CODE_PATH).'exercise/result.php?';
$attempt_url .= api_get_cidreq().'&show_headers=1&'; $attempt_url .= api_get_cidreq().'&show_headers=1&';
$attempt_url .= http_build_query([ $attempt_url .= http_build_query(['id' => $attempt_result['exe_id']]);
'id' => $attempt_result['exe_id'],
]);
$attempt_url .= $url_suffix; $attempt_url .= $url_suffix;
$attempt_link = Display::url( $attempt_link = Display::url(
@ -226,7 +255,7 @@ if (!empty($attempts)) {
), ),
'userIp' => $attempt_result['user_ip'], 'userIp' => $attempt_result['user_ip'],
]; ];
$attempt_link .= '&nbsp;&nbsp;&nbsp;'.$teacher_revised; $attempt_link .= PHP_EOL.$teacher_revised;
if (in_array( if (in_array(
$objExercise->results_disabled, $objExercise->results_disabled,
@ -359,24 +388,72 @@ if (!empty($attempts)) {
$row++; $row++;
} }
} }
$tableContent = $table->toHtml(); $table_content = $table->toHtml();
} }
if ($objExercise->selectAttempts()) { if ($objExercise->selectAttempts()) {
$list['attempts'] = $objExercise->selectAttempts(); $attempt_message = get_lang('Attempts').' '.$counter.' / '.$objExercise->selectAttempts();
if ($counter == $objExercise->selectAttempts()) {
$attempt_message = Display::return_message($attempt_message, 'error');
} else {
$attempt_message = Display::return_message($attempt_message, 'info');
}
if ($visible_return['value'] == true) {
$message .= $attempt_message;
}
} }
if ($time_control) { if ($time_control) {
$tpl->assign('time_control', $objExercise->returnTimeLeftDiv()); $html .= $objExercise->returnTimeLeftDiv();
}
$html .= $message;
$disable = api_get_configuration_value('exercises_disable_new_attempts');
if ($disable && empty($exercise_stat_info)) {
$exercise_url_button = Display::return_message(get_lang('NewExerciseAttemptDisabled'));
}
$isLimitReached = ExerciseLib::isQuestionsLimitPerDayReached(
api_get_user_id(),
count($objExercise->get_validated_question_list()),
api_get_course_int_id(),
api_get_session_id()
);
if (!empty($exercise_url_button) && !$isLimitReached) {
$html .= Display::div(
Display::div(
$exercise_url_button,
['class' => 'exercise_overview_options']
),
['class' => 'options']
);
} }
$tpl->assign('fluid', $fluid); if ($isLimitReached) {
$tpl->assign('data', $list); $maxQuestionsAnswered = (int) api_get_course_setting('quiz_question_limit_per_day');
$tpl->assign('label', $label);
$tpl->assign('message', $message); $html .= Display::return_message(
$tpl->assign('count', $counter); sprintf(get_lang('QuizQuestionsLimitPerDayXReached'), $maxQuestionsAnswered),
$tpl->assign('status', $msgStatus); 'warning',
$tpl->assign('table_result', $tableContent); false
$layout = $tpl->get_template('exercise/overview.tpl'); );
$tpl->assign('content', $tpl->fetch($layout)); }
$tpl->display_one_col_template();
$html .= Display::tag(
'div',
$table_content,
['class' => 'table-responsive']
);
$html .= '</div>';
if ($certificateBlock) {
$html .= PHP_EOL.$certificateBlock;
}
echo $html;
Display::display_footer();

@ -2938,14 +2938,6 @@ class DocumentManager
// When sending zip files // When sending zip files
if ($new_path === true && $unzip == 1) { if ($new_path === true && $unzip == 1) {
if ($show_output) {
echo Display::return_message(
get_lang('UplUploadSucceeded').'<br />',
'confirm',
false
);
}
return [ return [
'title' => $files[$fileKey]['name'], 'title' => $files[$fileKey]['name'],
'url' => '#', 'url' => '#',
@ -5202,7 +5194,7 @@ class DocumentManager
$url = 'show_content.php?'.$courseParams.'&id='.$document_data['id']; $url = 'show_content.php?'.$courseParams.'&id='.$document_data['id'];
$class = 'ajax'; $class = 'ajax';
if ($visibility == false) { if ($visibility == false) {
$class = "ajax text-muted"; $class = 'ajax text-muted';
} }
return Display::url( return Display::url(

@ -1899,7 +1899,7 @@ class ExtraField extends Model
$deleteId = $field_details['variable'].'_delete'; $deleteId = $field_details['variable'].'_delete';
$form->addHtml(" $form->addHtml("
<script> <script>
$(document).ready(function() { $(function() {
$('#".$deleteId."').on('click', function() { $('#".$deleteId."').on('click', function() {
$.ajax({ $.ajax({
type: 'GET', type: 'GET',

File diff suppressed because one or more lines are too long

@ -53,6 +53,10 @@ class MySpace
'url' => api_get_path(WEB_CODE_PATH).'mySpace/exercise_category_report.php', 'url' => api_get_path(WEB_CODE_PATH).'mySpace/exercise_category_report.php',
'content' => get_lang('ExerciseCategoryAllSessionsReport'), 'content' => get_lang('ExerciseCategoryAllSessionsReport'),
], ],
[
'url' => api_get_path(WEB_CODE_PATH).'mySpace/survey_report.php',
'content' => get_lang('SurveyReport'),
],
]; ];
return Display::actions($actions, null); return Display::actions($actions, null);
@ -63,8 +67,8 @@ class MySpace
*/ */
public static function getTopMenu() public static function getTopMenu()
{ {
$menu_items = []; $menuItems = [];
$menu_items[] = Display::url( $menuItems[] = Display::url(
Display::return_icon( Display::return_icon(
'statistics.png', 'statistics.png',
get_lang('MyStats'), get_lang('MyStats'),
@ -73,7 +77,7 @@ class MySpace
), ),
api_get_path(WEB_CODE_PATH)."auth/my_progress.php" api_get_path(WEB_CODE_PATH)."auth/my_progress.php"
); );
$menu_items[] = Display::url( $menuItems[] = Display::url(
Display::return_icon( Display::return_icon(
'teacher.png', 'teacher.png',
get_lang('TeacherInterface'), get_lang('TeacherInterface'),
@ -82,7 +86,7 @@ class MySpace
), ),
api_get_path(WEB_CODE_PATH).'mySpace/?view=teacher' api_get_path(WEB_CODE_PATH).'mySpace/?view=teacher'
); );
$menu_items[] = Display::url( $menuItems[] = Display::url(
Display::return_icon( Display::return_icon(
'star_na.png', 'star_na.png',
get_lang('AdminInterface'), get_lang('AdminInterface'),
@ -91,12 +95,12 @@ class MySpace
), ),
'#' '#'
); );
$menu_items[] = Display::url( $menuItems[] = Display::url(
Display::return_icon('quiz.png', get_lang('ExamTracking'), [], 32), Display::return_icon('quiz.png', get_lang('ExamTracking'), [], 32),
api_get_path(WEB_CODE_PATH).'tracking/exams.php' api_get_path(WEB_CODE_PATH).'tracking/exams.php'
); );
$menu = ''; $menu = '';
foreach ($menu_items as $item) { foreach ($menuItems as $item) {
$menu .= $item; $menu .= $item;
} }
$menu .= '<br />'; $menu .= '<br />';
@ -207,14 +211,16 @@ class MySpace
} }
// protect data // protect data
$user_id = intval($user_id); $user_id = (int) $user_id;
$session_id = intval($session_id); $session_id = (int) $session_id;
$new_course_list = []; $new_course_list = [];
foreach ($course_list as $course_item) { foreach ($course_list as $course_item) {
$courseInfo = api_get_course_info($course_item['code']); $courseInfo = api_get_course_info($course_item['code']);
if ($courseInfo) {
$courseId = $courseInfo['real_id']; $courseId = $courseInfo['real_id'];
$new_course_list[] = '"'.$courseId.'"'; $new_course_list[] = '"'.$courseId.'"';
} }
}
$course_list = implode(', ', $new_course_list); $course_list = implode(', ', $new_course_list);
if (empty($course_list)) { if (empty($course_list)) {
@ -321,80 +327,27 @@ class MySpace
public static function display_tracking_user_overview() public static function display_tracking_user_overview()
{ {
self::display_user_overview_export_options(); self::display_user_overview_export_options();
$t_head = '<table style="width: 100%;border:0;padding:0;border-collapse:collapse;table-layout: fixed">';
$t_head .= '<tr>'; $params = ['view' => 'admin', 'display' => 'user'];
$t_head .= '<th width="155px" style="border-left:0;border-bottom:0"><span>'.get_lang('Course').'</span></th>';
$t_head .= '<th style="padding:0;border-bottom:0"><span>'.cut(get_lang('AvgTimeSpentInTheCourse'), 6, true).'</span></th>';
$t_head .= '<th style="padding:0;border-bottom:0"><span>'.cut(get_lang('AvgStudentsProgress'), 6, true).'</span></th>';
$t_head .= '<th style="padding:0;border-bottom:0"><span>'.cut(get_lang('AvgCourseScore'), 6, true).'</span></th>';
$t_head .= '<th style="padding:0;border-bottom:0"><span>'.cut(get_lang('TotalNumberOfMessages'), 6, true).'</span></th>';
$t_head .= '<th style="padding:0;border-bottom:0"><span>'.cut(get_lang('TotalNumberOfAssignments'), 6, true).'</span></th>';
$t_head .= '<th width="105px" style="border-bottom:0"><span>'.get_lang('TotalExercisesScoreObtained').'</span></th>';
$t_head .= '<th style="padding:0;border-bottom:0"><span>'.cut(get_lang('TotalExercisesAnswered'), 6, true).'</span></th>';
$t_head .= '<th style="padding:0;border-bottom:0;border-right:0;"><span>'.get_lang('LatestLogin').'</span></th>';
$t_head .= '</tr></table>';
$addparams = ['view' => 'admin', 'display' => 'user'];
$table = new SortableTable( $table = new SortableTable(
'tracking_user_overview', 'tracking_user_overview',
['MySpace', 'get_number_of_users_tracking_overview'], ['MySpace', 'get_number_of_users_tracking_overview'],
['MySpace', 'get_user_data_tracking_overview'], ['MySpace', 'get_user_data_tracking_overview'],
0
);
$table->additional_parameters = $addparams;
$table->set_header(
0, 0,
get_lang('OfficialCode'), 20,
true, 'ASC',
['style' => 'font-size:8pt'], null, [
['style' => 'font-size:8pt'] 'class' => 'table table-transparent',
); ]
if (api_is_western_name_order()) {
$table->set_header(
1,
get_lang('FirstName'),
true,
['style' => 'font-size:8pt'],
['style' => 'font-size:8pt']
);
$table->set_header(
2,
get_lang('LastName'),
true,
['style' => 'font-size:8pt'],
['style' => 'font-size:8pt']
);
} else {
$table->set_header(
1,
get_lang('LastName'),
true,
['style' => 'font-size:8pt'],
['style' => 'font-size:8pt']
);
$table->set_header(
2,
get_lang('FirstName'),
true,
['style' => 'font-size:8pt'],
['style' => 'font-size:8pt']
);
}
$table->set_header(
3,
get_lang('LoginName'),
true,
['style' => 'font-size:8pt'],
['style' => 'font-size:8pt']
);
$table->set_header(
4,
$t_head,
false,
['style' => 'width:90%;border:0;padding:0;font-size:7.5pt;'],
['style' => 'width:90%;padding:0;font-size:7.5pt;']
); );
$table->set_column_filter(4, ['MySpace', 'course_info_tracking_filter']); $table->additional_parameters = $params;
$table->display();
$table->set_column_filter(0, ['MySpace', 'returnTrackingUserOverviewFilter']);
$tableContent = $table->return_table();
$tpl = new Template('', false, false, false, false, false, false);
$tpl->assign('table', $tableContent);
$templateName = $tpl->get_template('my_space/user_summary.tpl');
$tpl->display($templateName);
} }
/** /**
@ -415,19 +368,19 @@ class MySpace
$order = [ $order = [
0 => 'firstname', 0 => 'firstname',
1 => 'lastname', 1 => 'lastname',
2 => ($sort_by_first_name ? 'firstname' : 'lastname'), 2 => $sort_by_first_name ? 'firstname' : 'lastname',
3 => 'login_date', 3 => 'login_date',
4 => ($sort_by_first_name ? 'firstname' : 'lastname'), 4 => $sort_by_first_name ? 'firstname' : 'lastname',
5 => ($sort_by_first_name ? 'firstname' : 'lastname'), 5 => $sort_by_first_name ? 'firstname' : 'lastname',
]; ];
} else { } else {
$order = [ $order = [
0 => 'lastname', 0 => 'lastname',
1 => 'firstname', 1 => 'firstname',
2 => ($sort_by_first_name ? 'firstname' : 'lastname'), 2 => $sort_by_first_name ? 'firstname' : 'lastname',
3 => 'login_date', 3 => 'login_date',
4 => ($sort_by_first_name ? 'firstname' : 'lastname'), 4 => $sort_by_first_name ? 'firstname' : 'lastname',
5 => ($sort_by_first_name ? 'firstname' : 'lastname'), 5 => $sort_by_first_name ? 'firstname' : 'lastname',
]; ];
} }
$table = new SortableTable( $table = new SortableTable(
@ -514,7 +467,7 @@ class MySpace
} }
} }
if (!empty($order[$tracking_column])) { if (!empty($order[$tracking_column])) {
$sqlCoachs .= " ORDER BY ".$order[$tracking_column]." ".$tracking_direction; $sqlCoachs .= ' ORDER BY '.$order[$tracking_column].' '.$tracking_direction;
} }
$result_coaches = Database::query($sqlCoachs); $result_coaches = Database::query($sqlCoachs);
@ -664,6 +617,8 @@ class MySpace
/** /**
* Display a sortable table that contains an overview off all the progress of the user in a session. * Display a sortable table that contains an overview off all the progress of the user in a session.
* *
* @deprecated ?
*
* @author César Perales <cesar.perales@beeznest.com>, Beeznest Team * @author César Perales <cesar.perales@beeznest.com>, Beeznest Team
*/ */
public static function display_tracking_lp_progress_overview( public static function display_tracking_lp_progress_overview(
@ -784,6 +739,8 @@ class MySpace
* *
* @return string HTML array of results formatted for gridJS * @return string HTML array of results formatted for gridJS
* *
* @deprecated ?
*
* @author César Perales <cesar.perales@beeznest.com>, Beeznest Team * @author César Perales <cesar.perales@beeznest.com>, Beeznest Team
*/ */
public static function display_tracking_exercise_progress_overview( public static function display_tracking_exercise_progress_overview(
@ -986,31 +943,26 @@ class MySpace
*/ */
public static function display_tracking_course_overview() public static function display_tracking_course_overview()
{ {
$t_head = '<table style="width: 100%;border:0;padding:0;border-collapse:collapse;table-layout: fixed">'; $params = ['view' => 'admin', 'display' => 'courseoverview'];
$t_head .= '<tr>';
$t_head .= '<th style="padding:0;border-bottom:0"><span>'.cut(get_lang('AvgTimeSpentInTheCourse'), 6, true).'</span></th>';
$t_head .= '<th style="padding:0;border-bottom:0"><span>'.cut(get_lang('AvgStudentsProgress'), 6, true).'</span></th>';
$t_head .= '<th style="padding:0;border-bottom:0"><span>'.cut(get_lang('AvgCourseScore'), 6, true).'</span></th>';
$t_head .= '<th style="padding:0;border-bottom:0"><span>'.cut(get_lang('TotalNumberOfMessages'), 6, true).'</span></th>';
$t_head .= '<th style="padding:0;border-bottom:0"><span>'.cut(get_lang('TotalNumberOfAssignments'), 6, true).'</span></th>';
$t_head .= '<th width="105px" style="border-bottom:0"><span>'.get_lang('TotalExercisesScoreObtained').'</span></th>';
$t_head .= '<th style="padding:0;border-bottom:0"><span>'.cut(get_lang('TotalExercisesAnswered'), 6, true).'</span></th>';
$t_head .= '<th style="padding:0;border-bottom:0;border-right:0;"><span>'.get_lang('LatestLogin').'</span></th>';
$t_head .= '</tr></table>';
$addparams = ['view' => 'admin', 'display' => 'courseoverview'];
$table = new SortableTable( $table = new SortableTable(
'tracking_session_overview', 'tracking_session_overview',
['MySpace', 'get_total_number_courses'], ['MySpace', 'get_total_number_courses'],
['MySpace', 'get_course_data_tracking_overview'], ['MySpace', 'get_course_data_tracking_overview'],
1 1,
20,
'ASC',
null, [
'class' => 'table table-transparent',
]
); );
$table->additional_parameters = $addparams; $table->additional_parameters = $params;
$table->set_column_filter(0, ['MySpace', 'course_tracking_filter']);
$tableContent = $table->return_table();
$table->set_header(0, '', false, null, ['style' => 'display: none']); $tpl = new Template('', false, false, false, false, false, false);
$table->set_header(1, get_lang('Course'), true, ['style' => 'font-size:8pt'], ['style' => 'font-size:8pt']); $tpl->assign('table', $tableContent);
$table->set_header(2, $t_head, false, ['style' => 'width:90%;border:0;padding:0;font-size:7.5pt;'], ['style' => 'width:90%;padding:0;font-size:7.5pt;']); $templateName = $tpl->get_template('my_space/course_summary.tpl');
$table->set_column_filter(2, ['MySpace', 'course_tracking_filter']); $tpl->display($templateName);
$table->display();
} }
/** /**
@ -1054,8 +1006,6 @@ class MySpace
$list[] = [ $list[] = [
'0' => $course['code'], '0' => $course['code'],
'col0' => $course['code'], 'col0' => $course['code'],
'1' => $course['title'],
'col1' => $course['title'],
]; ];
} }
@ -1077,8 +1027,8 @@ class MySpace
$courseInfo = api_get_course_info($course_code); $courseInfo = api_get_course_info($course_code);
$courseId = $courseInfo['real_id']; $courseId = $courseInfo['real_id'];
// the table header $tpl = new Template('', false, false, false, false, false, false);
$return = '<table class="data_table" style="width: 100%;border:0;padding:0;border-collapse:collapse;table-layout: fixed">'; $data = null;
// database table definition // database table definition
$tbl_course_rel_user = Database::get_main_table(TABLE_MAIN_COURSE_USER); $tbl_course_rel_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
@ -1146,9 +1096,8 @@ class MySpace
$last_login_date == false $last_login_date == false
) { // TODO: To be cleaned ) { // TODO: To be cleaned
$last_login_date = $last_login_date_tmp; $last_login_date = $last_login_date_tmp;
} elseif ($last_login_date_tmp != false && } elseif ($last_login_date_tmp != false && $last_login_date != false) {
$last_login_date != false // TODO: Repeated previous condition. To be cleaned.
) { // TODO: Repeated previous condition. To be cleaned.
// Find the max and assign it to first_login_date // Find the max and assign it to first_login_date
if (strtotime($last_login_date_tmp) > strtotime($last_login_date)) { if (strtotime($last_login_date_tmp) > strtotime($last_login_date)) {
$last_login_date = $last_login_date_tmp; $last_login_date = $last_login_date_tmp;
@ -1189,26 +1138,30 @@ class MySpace
} else { } else {
$total_score = '-'; $total_score = '-';
} }
$return .= '<tr>';
// time spent in the course
$return .= ' <td style="width:164px;">'.api_time_to_hms($time_spent).'</td>';
// student progress in course
$return .= ' <td>'.$avg_progress.'</td>';
// student score
$return .= ' <td>'.$avg_score.'</td>';
// student messages
$return .= ' <td>'.$nb_messages.'</td>';
// student assignments
$return .= ' <td>'.$nb_assignments.'</td>';
// student exercises results (obtained score, maximum score, number of exercises answered, score percentage)
$return .= '<td width="105px;">'.$total_score.'</td>';
$return .= '<td>'.$total_questions_answered.'</td>';
// last connection
$return .= ' <td>'.$last_login_date.'</td>';
$return .= '</tr>';
$return .= '</table>';
return $return; $data = [
'course_code' => $course_code,
'id' => $courseId,
'image' => $courseInfo['course_image_large'],
'image_small' => $courseInfo['course_image'],
'title' => $courseInfo['title'],
'url' => $courseInfo['course_public_url'],
'category' => $courseInfo['categoryName'],
'time_spent' => api_time_to_hms($time_spent),
'avg_progress' => $avg_progress,
'avg_score' => $avg_score,
'number_message' => $nb_messages,
'number_assignments' => $nb_assignments,
'total_score' => $total_score,
'questions_answered' => $total_questions_answered,
'last_login' => $last_login_date,
];
$tpl->assign('data', $data);
$layout = $tpl->get_template('my_space/partials/tracking_course_overview.tpl');
$content = $tpl->fetch($layout);
return $content;
} }
/** /**
@ -1415,14 +1368,14 @@ class MySpace
$head .= '<th style="padding:0;border-bottom:0;border-right:0;"><span>'.get_lang('LatestLogin').'</span></th>'; $head .= '<th style="padding:0;border-bottom:0;border-right:0;"><span>'.get_lang('LatestLogin').'</span></th>';
$head .= '</tr></table>'; $head .= '</tr></table>';
$addparams = ['view' => 'admin', 'display' => 'sessionoverview']; $params = ['view' => 'admin', 'display' => 'sessionoverview'];
$table = new SortableTable( $table = new SortableTable(
'tracking_session_overview', 'tracking_session_overview',
['MySpace', 'get_total_number_sessions'], ['MySpace', 'get_total_number_sessions'],
['MySpace', 'get_session_data_tracking_overview'], ['MySpace', 'get_session_data_tracking_overview'],
1 1
); );
$table->additional_parameters = $addparams; $table->additional_parameters = $params;
$table->set_header(0, '', false, null, ['style' => 'display: none']); $table->set_header(0, '', false, null, ['style' => 'display: none']);
$table->set_header( $table->set_header(
@ -1644,21 +1597,19 @@ class MySpace
$tbl_user = Database::get_main_table(TABLE_MAIN_USER); $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
// the values of the sortable table // the values of the sortable table
$from = 0;
if ($_GET['tracking_session_overview_page_nr']) { if ($_GET['tracking_session_overview_page_nr']) {
$from = $_GET['tracking_session_overview_page_nr']; $from = $_GET['tracking_session_overview_page_nr'];
} else {
$from = 0;
} }
$orderby = 0;
if ($_GET['tracking_session_overview_column']) { if ($_GET['tracking_session_overview_column']) {
$orderby = $_GET['tracking_session_overview_column']; $orderby = $_GET['tracking_session_overview_column'];
} else {
$orderby = 0;
} }
$direction = 'ASC';
if ($_GET['tracking_session_overview_direction']) { if ($_GET['tracking_session_overview_direction']) {
$direction = $_GET['tracking_session_overview_direction']; $direction = $_GET['tracking_session_overview_direction'];
} else {
$direction = 'ASC';
} }
$session_data = self::get_session_data_tracking_overview($from, 1000, $orderby, $direction); $session_data = self::get_session_data_tracking_overview($from, 1000, $orderby, $direction);
@ -1835,6 +1786,7 @@ class MySpace
* *
* @param int $user_id the id of the user * @param int $user_id the id of the user
* @param string $course_code the course code * @param string $course_code the course code
* @param int $session_id
* *
* @return array * @return array
* *
@ -1844,15 +1796,20 @@ class MySpace
* *
* @since November 2008 * @since November 2008
*/ */
public static function exercises_results($user_id, $course_code, $session_id = false) public static function exercises_results($user_id, $course_code, $session_id = 0)
{ {
$user_id = (int) $user_id;
$courseId = api_get_course_int_id($course_code); $courseId = api_get_course_int_id($course_code);
$sql = 'SELECT exe_result, exe_weighting $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
FROM '.Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES)."
$sql = "SELECT exe_result, exe_weighting
FROM $table
WHERE WHERE
c_id = ' . $courseId . ' AND c_id = $courseId AND
exe_user_id = '".intval($user_id)."'"; exe_user_id = $user_id";
if ($session_id !== false) {
$session_id = (int) $session_id;
if (!empty($session_id)) {
$sql .= " AND session_id = '".$session_id."' "; $sql .= " AND session_id = '".$session_id."' ";
} }
$result = Database::query($sql); $result = Database::query($sql);
@ -1865,10 +1822,9 @@ class MySpace
$questions_answered++; $questions_answered++;
} }
$percentage = null;
if ($score_possible != 0) { if ($score_possible != 0) {
$percentage = round(($score_obtained / $score_possible * 100), 2); $percentage = round(($score_obtained / $score_possible * 100), 2);
} else {
$percentage = null;
} }
return [ return [
@ -1926,16 +1882,16 @@ class MySpace
$csv_row = []; $csv_row = [];
$csv_row[] = get_lang('OfficialCode'); $csv_row[] = get_lang('OfficialCode');
if ($is_western_name_order) { if ($is_western_name_order) {
$csv_row[] = get_lang('FirstName', ''); $csv_row[] = get_lang('FirstName');
$csv_row[] = get_lang('LastName', ''); $csv_row[] = get_lang('LastName');
} else { } else {
$csv_row[] = get_lang('LastName', ''); $csv_row[] = get_lang('LastName');
$csv_row[] = get_lang('FirstName', ''); $csv_row[] = get_lang('FirstName');
} }
$csv_row[] = get_lang('LoginName'); $csv_row[] = get_lang('LoginName');
$csv_row[] = get_lang('CourseCode'); $csv_row[] = get_lang('CourseCode');
// the additional user defined fields (only those that were selected to be exported)
// the additional user defined fields (only those that were selected to be exported)
$fields = UserManager::get_extra_fields(0, 50, 5, 'ASC'); $fields = UserManager::get_extra_fields(0, 50, 5, 'ASC');
$additionalExportFields = Session::read('additional_export_fields'); $additionalExportFields = Session::read('additional_export_fields');
@ -2163,6 +2119,7 @@ class MySpace
public static function get_user_data_tracking_overview($from, $numberItems, $column, $direction) public static function get_user_data_tracking_overview($from, $numberItems, $column, $direction)
{ {
$isWestern = api_is_western_name_order(); $isWestern = api_is_western_name_order();
switch ($column) { switch ($column) {
case '0': case '0':
$column = 'official_code'; $column = 'official_code';
@ -2227,9 +2184,9 @@ class MySpace
public static function get_user_overview_export_extra_fields($user_id) public static function get_user_overview_export_extra_fields($user_id)
{ {
// include the user manager // include the user manager
$extra_data = UserManager::get_extra_user_data($user_id, true); $data = UserManager::get_extra_user_data($user_id, true);
return $extra_data; return $data;
} }
/** /**
@ -2243,7 +2200,7 @@ class MySpace
* *
* @return array with the username, the sufix * @return array with the username, the sufix
* *
* @author Julio Montoya Armas * @author Julio Montoya
*/ */
public static function make_username($firstname, $lastname, $username, $language = null, $encoding = null) public static function make_username($firstname, $lastname, $username, $language = null, $encoding = null)
{ {
@ -2258,9 +2215,7 @@ class MySpace
} }
$desired_username = UserManager::create_username( $desired_username = UserManager::create_username(
$firstname, $firstname,
$lastname, $lastname
$language,
$encoding
); );
if (UserManager::is_username_available($desired_username.$sufix)) { if (UserManager::is_username_available($desired_username.$sufix)) {
break; break;
@ -2320,10 +2275,10 @@ class MySpace
{ {
$table_user = Database::get_main_table(TABLE_MAIN_USER); $table_user = Database::get_main_table(TABLE_MAIN_USER);
$tbl_session_rel_course_rel_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER); $tbl_session_rel_course_rel_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
$id_session = intval($id_session); $id_session = (int) $id_session;
$username = Database::escape_string($username); $username = Database::escape_string($username);
foreach ($course_list as $courseId) { foreach ($course_list as $courseId) {
$courseId = intval($courseId); $courseId = (int) $courseId;
$sql = " SELECT u.user_id FROM $tbl_session_rel_course_rel_user rel $sql = " SELECT u.user_id FROM $tbl_session_rel_course_rel_user rel
INNER JOIN $table_user u INNER JOIN $table_user u
ON (rel.user_id = u.user_id) ON (rel.user_id = u.user_id)
@ -2335,8 +2290,6 @@ class MySpace
$rs = Database::query($sql); $rs = Database::query($sql);
if (Database::num_rows($rs) > 0) { if (Database::num_rows($rs) > 0) {
return Database::result($rs, 0, 0); return Database::result($rs, 0, 0);
} else {
return 0;
} }
} }
@ -2412,9 +2365,8 @@ class MySpace
public static function get_user_creator($users) public static function get_user_creator($users)
{ {
$errors = []; $errors = [];
foreach ($users as $index => $user) {
// database table definition
$table_user = Database::get_main_table(TABLE_MAIN_USER); $table_user = Database::get_main_table(TABLE_MAIN_USER);
foreach ($users as $index => $user) {
$username = Database::escape_string($user['UserName']); $username = Database::escape_string($user['UserName']);
$sql = "SELECT creator_id FROM $table_user WHERE username='$username' "; $sql = "SELECT creator_id FROM $table_user WHERE username='$username' ";
@ -2528,24 +2480,20 @@ class MySpace
$users = $new_users; $users = $new_users;
// Inserting users. // Inserting users.
$super_list = [];
foreach ($course_list as $enreg_course) { foreach ($course_list as $enreg_course) {
$nbr_users = 0; $nbr_users = 0;
$new_users = []; $new_users = [];
$enreg_course = Database::escape_string($enreg_course); $enreg_course = Database::escape_string($enreg_course);
foreach ($users as $index => $user) { foreach ($users as $index => $user) {
$userid = intval($user['id']); $userid = (int) $user['id'];
$sql = "INSERT IGNORE INTO $tbl_session_rel_course_rel_user(session_id, c_id, user_id) $sql = "INSERT IGNORE INTO $tbl_session_rel_course_rel_user(session_id, c_id, user_id)
VALUES('$id_session','$enreg_course','$userid')"; VALUES('$id_session','$enreg_course','$userid')";
$course_session = ['course' => $enreg_course, 'added' => 1];
$result = Database::query($sql); $result = Database::query($sql);
if (Database::affected_rows($result)) { if (Database::affected_rows($result)) {
$nbr_users++; $nbr_users++;
} }
$new_users[] = $user; $new_users[] = $user;
} }
$super_list[] = $new_users;
//update the nbr_users field //update the nbr_users field
$sql_select = "SELECT COUNT(user_id) as nbUsers FROM $tbl_session_rel_course_rel_user $sql_select = "SELECT COUNT(user_id) as nbUsers FROM $tbl_session_rel_course_rel_user
@ -2575,7 +2523,6 @@ class MySpace
// Sending emails. // Sending emails.
$addedto = ''; $addedto = '';
if ($sendMail) { if ($sendMail) {
$i = 0;
foreach ($users as $index => $user) { foreach ($users as $index => $user) {
$emailsubject = '['.api_get_setting('siteName').'] '.get_lang('YourReg').' '.api_get_setting('siteName'); $emailsubject = '['.api_get_setting('siteName').'] '.get_lang('YourReg').' '.api_get_setting('siteName');
$emailbody = get_lang('Dear').' '. $emailbody = get_lang('Dear').' '.
@ -2615,7 +2562,6 @@ class MySpace
$registered_users .= UserManager::getUserProfileLink($userInfo).' - '.$addedto.'<br />'; $registered_users .= UserManager::getUserProfileLink($userInfo).' - '.$addedto.'<br />';
} }
} else { } else {
$i = 0;
foreach ($users as $index => $user) { foreach ($users as $index => $user) {
$userInfo = api_get_user_info($user['id']); $userInfo = api_get_user_info($user['id']);
if (($user['added_at_platform'] == 1 && $user['added_at_session'] == 1) || $user['added_at_session'] == 1) { if (($user['added_at_platform'] == 1 && $user['added_at_session'] == 1) || $user['added_at_session'] == 1) {
@ -2720,7 +2666,7 @@ class MySpace
if (!empty($studentId)) { if (!empty($studentId)) {
$student = api_get_user_entity($studentId); $student = api_get_user_entity($studentId);
if ($student) { if ($student) {
$studentList[$student->getId()] = $student->getCompleteName(); $studentList[$student->getId()] = UserManager::formatUserFullName($student);
} }
} }
@ -2815,9 +2761,6 @@ class MySpace
['MySpace', 'getUserDataAccessTrackingOverview'], ['MySpace', 'getUserDataAccessTrackingOverview'],
0 0
); );
//$table->additional_parameters = $form->exportValues();
$table->set_header(0, get_lang('LoginDate'), true); $table->set_header(0, get_lang('LoginDate'), true);
$table->set_header(1, get_lang('Username'), true); $table->set_header(1, get_lang('Username'), true);
if (api_is_western_name_order()) { if (api_is_western_name_order()) {
@ -2876,6 +2819,11 @@ class MySpace
$column, $column,
$orderDirection $orderDirection
) { ) {
$from = (int) $from;
$numberItems = (int) $numberItems;
$column = (int) $column;
$orderDirection = Database::escape_string($orderDirection);
$user = Database::get_main_table(TABLE_MAIN_USER); $user = Database::get_main_table(TABLE_MAIN_USER);
$course = Database::get_main_table(TABLE_MAIN_COURSE); $course = Database::get_main_table(TABLE_MAIN_COURSE);
$track_e_login = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN); $track_e_login = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
@ -2924,11 +2872,6 @@ class MySpace
$sql .= " AND u.user_id = ".$userId; $sql .= " AND u.user_id = ".$userId;
} }
if (isset($_GET['student_id']) && !empty($_GET['student_id'])) {
$userId = (int) $_GET['student_id'];
$sql .= " AND u.user_id = ".$userId;
}
if (isset($_GET['date']) && !empty($_GET['date'])) { if (isset($_GET['date']) && !empty($_GET['date'])) {
$dates = DateRangePicker::parseDateRange($_GET['date']); $dates = DateRangePicker::parseDateRange($_GET['date']);
if (isset($dates['start']) && !empty($dates['start'])) { if (isset($dates['start']) && !empty($dates['start'])) {
@ -3012,10 +2955,16 @@ class MySpace
$user_id = (int) $user_id; $user_id = (int) $user_id;
$connections = []; $connections = [];
if (!empty($course_info)) { if (!empty($course_info)) {
$courseId = intval($course_info['real_id']); $courseId = (int) $course_info['real_id'];
$end_date = add_day_to($end_date); $end_date = add_day_to($end_date);
$start_date = Database::escape_string($start_date);
$end_date = Database::escape_string($end_date);
$sessionCondition = api_get_session_condition($sessionId); $sessionCondition = api_get_session_condition($sessionId);
$sql = "SELECT login_course_date, logout_course_date $sql = "SELECT
login_course_date,
logout_course_date,
TIMESTAMPDIFF(SECOND, login_course_date, logout_course_date) duration
FROM $table FROM $table
WHERE WHERE
user_id = $user_id AND user_id = $user_id AND
@ -3030,6 +2979,7 @@ class MySpace
$connections[] = [ $connections[] = [
'login' => $row['login_course_date'], 'login' => $row['login_course_date'],
'logout' => $row['logout_course_date'], 'logout' => $row['logout_course_date'],
'duration' => $row['duration'],
]; ];
} }
} }
@ -3056,11 +3006,15 @@ function get_stats($user_id, $course_info, $sessionId, $start_date = null, $end_
$stringEndDate = ''; $stringEndDate = '';
if ($start_date != null && $end_date != null) { if ($start_date != null && $end_date != null) {
$end_date = add_day_to($end_date); $end_date = add_day_to($end_date);
$start_date = Database::escape_string($start_date);
$end_date = Database::escape_string($end_date);
$stringStartDate = "AND login_course_date BETWEEN '$start_date' AND '$end_date'"; $stringStartDate = "AND login_course_date BETWEEN '$start_date' AND '$end_date'";
$stringEndDate = "AND logout_course_date BETWEEN '$start_date' AND '$end_date'"; $stringEndDate = "AND logout_course_date BETWEEN '$start_date' AND '$end_date'";
} }
$user_id = intval($user_id); $user_id = (int) $user_id;
$courseId = intval($course_info['real_id']); $courseId = (int) $course_info['real_id'];
$sessionCondition = api_get_session_condition($sessionId); $sessionCondition = api_get_session_condition($sessionId);
$sql = "SELECT $sql = "SELECT
SEC_TO_TIME(AVG(time_to_sec(timediff(logout_course_date,login_course_date)))) as avrg, SEC_TO_TIME(AVG(time_to_sec(timediff(logout_course_date,login_course_date)))) as avrg,
@ -3092,34 +3046,12 @@ function get_stats($user_id, $course_info, $sessionId, $start_date = null, $end_
function add_day_to($end_date) function add_day_to($end_date)
{ {
$foo_date = strtotime($end_date); $foo_date = strtotime($end_date);
$foo_date = strtotime(" +1 day", $foo_date); $foo_date = strtotime(' +1 day', $foo_date);
$foo_date = date("Y-m-d", $foo_date); $foo_date = date('Y-m-d', $foo_date);
return $foo_date; return $foo_date;
} }
/**
* @param array
*
* @author Jorge Frisancho Jibaja
*
* @version OCT-22- 2010
*
* @return array
*/
function convert_to_array($sql_result)
{
$result_to_print = '<table>';
foreach ($sql_result as $key => $data) {
$result_to_print .= '<tr><td>'.date('d-m-Y (H:i:s)', $data['login']).'</td><td>'.
api_time_to_hms($data['logout'] - $data['login']).'</tr></td>'."\n";
}
$result_to_print .= '</table>';
$result_to_print = ["result" => $result_to_print];
return $result_to_print;
}
/** /**
* Converte an array to a table in html. * Converte an array to a table in html.
* *

@ -1,5 +1,4 @@
<?php <?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
/** /**
* Class to dynamically create an HTML SELECT * Class to dynamically create an HTML SELECT

@ -4661,7 +4661,6 @@ class SessionManager
*/ */
public static function countSessionsByEndDate($date = null) public static function countSessionsByEndDate($date = null)
{ {
$count = 0;
$sessionTable = Database::get_main_table(TABLE_MAIN_SESSION); $sessionTable = Database::get_main_table(TABLE_MAIN_SESSION);
$url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION); $url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
$date = Database::escape_string($date); $date = Database::escape_string($date);

@ -8301,6 +8301,125 @@ $DifferenceOfDaysAndCalendar = "Difference between days and calendar";
$SkillUserList = "Skills and users list"; $SkillUserList = "Skills and users list";
$CourseId = "Course ID"; $CourseId = "Course ID";
$CareDetailView = "Student care detail view"; $CareDetailView = "Student care detail view";
$MoreDataAvailableInTheDatabaseButTrunkedForEfficiencyReasons = "More data available in the database but trunked for efficiency reasons.";
$SendAnnouncementCopyToMyself = "Send a copy by email to myself.";
$CourseHoursDuration = "Course duration (h)";
$AnnouncementWillBeSentTo = "Announcement will be sent to";
$FrmReadOutTextIntro = "You need attach a audio file according to the text, clicking on the %s icon.";
$CreateReadOutText = "Create read-out text";
$OutstandingStudents = "Outstanding students";
$PercentileScoresDistribution = "Percentile scores distribution";
$ProgressObtainedFromLPProgressAndTestsAverage = "Note: This progress is obtained through a combination of progress in the learning paths and average scores in the tests";
$CreateNewSurveyDoodle = "Create a new Doodle type survey";
$RemoveMultiplicateQuestions = "Remove multiplicated questions";
$MultiplicateQuestions = "Multiplicate questions";
$QuestionTags = "You can use the tags {{class_name}} and {{student_full_name}} in the question to be able to multiplicate questions.
On the survey's list page in the action field you have a button to multiplicate question that will look for the {{class_name}} tag and duplicate the question for all the class subscribed to the course and rename it with the name of the class.
It will also add a page ending to make a new page for each class.
Then it will look for the {{student_full_name}} tag and duplicate the question for all the student in the class (for each class) and rename it with the student's full name.";
$CreateMeeting = "Create meeting poll";
$QuestionForNextClass = "Question for next class";
$NewExerciseAttemptDisabled = "The portal do not allowed to start new test for the moment, please come back later.";
$PersonalDataOfficer = "Personal data officer";
$MaxNumberSubscribedStudentsReached = "The maximum number of student has already been reached, it is not possible to subscribe more student.";
$UserXAddedToCourseX = "User %s has been registered to course %s";
$LpMinTime = "Minimum time (minutes)";
$LpMinTimeDescription = "Minimum time (in minutes) a student must remain in the learning path to get access to the next one.";
$LpMinTimeWarning = "You didn't spend the minimum time required in the learning path.";
$YouHaveSpentXTime = "You have spent %s";
$TimeSpentInLp = "Time spent in the learning path";
$StudentXFinishedLp = "Student %s has completed his/her learning paths.";
$IHaveFinishedTheLessonsNotifyTheTeacher = "I have finished the lessons, notify the teacher";
$TheStudentXHasFinishedTheCourseYLearningPaths = "The student <b>%s</b> has finished the learning paths from the course <b>%s</b>";
$ThisQuestionExistsInAnotherExercisesWarning = "This question is used in another exercises. If you continue its edition, the changes will affect all exercises that contain this question.";
$TimeSpentTimeRequired = "Time spent / Time required";
$ProgressSpentInLp = "Progress";
$InSessionXYouHadTheFollowingResults = "In session %s, you had the following results";
$TimeSpent = "Time spent";
$NumberOfVisits = "Number of visits";
$FormationUnit = "Formation unit";
$GlobalProgress = "Global progress";
$AttestationOfAttendance = "Attestation of attendance";
$DontShowScoreOnlyWhenUserFinishesAllAttemptsButShowFeedbackEachAttempt = "Do not show the score (only when user finishes all attempts) but show feedback for each attempt.";
$ExerciseRankingMode = "Ranking mode: Do not show results details question by question and show a table with the ranking of all other users.";
$BackToTop = "Back to top";
$PersonalDataTreatmentTitleHelp = "What do we consider personal data?";
$PersonalDataCollectionTitleHelp = "Why do we collect this data?";
$PersonalDataRecordingTitleHelp = "Where do we record the data? (in which system, hosted where, accessible by which company that handles the servers, etc.)";
$PersonalDataOrganizationTitleHelp = "How is the data structured? To what extent does the organization of the data protect your security? ... Do you have established processes that ensure that a hacker will not be able to collect all the personal data on the first attempt? Are they encrypted?";
$PersonalDataStructureTitleHelp = "How is the data structured in this software? (one tab for all the data, it has some categorization / label, ...)";
$PersonalDataConservationTitleHelp = "How long do we save the data? Is there data that expires even though the account is still active? How much time do we keep the data after the last use of the system?";
$PersonalDataAdaptationTitleHelp = "What changes can we make to the data? What changes can be made to the data without affecting the service?";
$PersonalDataExtractionTitleHelp = "What do we extract data for (towards other internal processes) and which data is it?";
$PersonalDataConsultationTitleHelp = "Who can consult the personal data? For what purpose?";
$PersonalDataUsageTitleHelp = "How and for what can we use the personal data?";
$PersonalDataCommunicationTitleHelp = "With whom can we share them? In what opportunities? Through what processes (safe or not)?";
$PersonalDataInterconnectionTitleHelp = "Do we have another system with which Chamilo interacts? What is the interconnection process? What are the data pieces exchanged and in what way?";
$PersonalDataLimitationTitleHelp = "What are the limits that we will always respect when using personal data? How far can we go?";
$PersonalDataDeletionTitleHelp = "After how long do we erase the data? (event, last use, contract validity, etc.) What are the elimination processes?";
$PersonalDataDestructionTitleHelp = "What happens if the data is destroyed as a result of a technical failure? (unauthorized deletion or loss of material, for example)";
$PersonalDataProfilingTitleHelp = "For what purpose do we process personal data? Do we use it to filter the access users have to certain parts of our application? (negative or positive discrimination)";
$SearchUsersByName = "By name";
$GlobalChat = "Chat";
$AskRevision = "Ask for a revision";
$FeatureDisabledBecauseOfUnmaintainedThirdPartyLibraries = "This feature has been disabled because the libraries it depends on are no longer unmaintained.";
$SearchUserByGeolocalization = "By geolocalization";
$GiveRevision = "Give revision";
$ForumPostReported = "Post reported";
$UserXReportedPostXInForumX = "User %s has reported the message %s in the forum %s";
$MyCommunities = "My communities";
$SeeAllCommunities = "See all communities";
$HideForumNotifications = "Hide forum notifications";
$Forum_categoryFields = "Forum Category fields";
$Forum_postFields = "Forum post fields";
$ExerciseShowOnlyGlobalScoreAndCorrectAnswers = "Show only global score (not question score) and show only the correct answers, do not show incorrect answers at all";
$Reported = "Reported";
$FromTimeX = "From %s";
$ToTimeX = "To %s";
$SurveyInviteLink = "Survey invitation link";
$RevisionProposed = "Revision proposed";
$SessionsPlanCalendar = "Sessions plan calendar";
$QuizQuestionsLimitPerDay = "Limit of questions per day";
$QuizQuestionsLimitPerDayComment = "If set to greater than 0, this option will prevent the learner from entering a test that has more than what remains for the daily allowance. For example, is the limit is 50 and the learner has already taken 2 tests of 20 questions, it will not let the learner enter another 20 questions test (20+20+20=60 > 50). However, it will let the learner enter a 10 questions test (20+20+10=50).";
$QuizQuestionsLimitPerDayXReached = "Sorry, you have reached the maximum number of questions (%s) for the day. Please try again tomorrow.";
$VoteLike = "Like";
$VoteDislike = "Dislike";
$GenerateStats = "Generate statistics";
$BasicCourseDocuments = "Basic course documents";
$WeekX = "Week %s";
$QuizWrongAnswerHereIsTheCorrectOne = "Wrong answer. The correct one was:";
$ThisQuizCanBeEmbeddable = "This quiz can be embeddable on videos or mobile content";
$SearchGeolocalization = "Search for this location";
$LegalTermsAgreementStatus = "Terms of use agreement status";
$HideSurveyInvitationLink = "Hide survey invitation link";
$LeaveAMessage = "Leave a message";
$OrphanQuestion = "Orphan question";
$WelcomeToPortalXInCourseSessionX = "Welcome to portal %s Course session: %s";
$WelcomeToPortalXInCourseSessionXCoursePartOfCareerX = "Welcome to portal %s in course %s, part of career %s";
$YourNextModule = "Your next module";
$FirstLesson = "First lesson";
$ImportCourseTeachersAsCourseCoach = "Import course teachers as course coach in the session";
$ResumeImport = "Resume import";
$Candidate = "Candidate";
$GeneralTotal = "General total";
$Domains = "Domains";
$ScormStartAttemptDate = "Date";
$LoginsByDate = "Logins by date";
$AllowHtaccessScormImport = "Allow htaccess in the SCORM import";
$ExerciseAutoEvaluationAndRankingMode = "Auto-evaluation mode and ranking";
$YouAreReceivingACopyBecauseYouAreACourseCoach = "You're receiving a copy because, you're a course coach";
$GenerateReport = "Generate report";
$VersionFromVersionFile = "Version from the version file";
$VersionFromConfigFile = "Version from the config file";
$TheVersionFromTheVersionFileIsUpdatedWithEachVersionIfMainInstallDirectoryIsPresent = "The version from the version.php file is updated with each version but only available if the main/install/ directory is present.";
$TheVersionFromTheConfigurationFileShowsOnTheAdminPageButHasToBeChangedManuallyOnUpgrade = "The version from the main configuration file shows on the main administration page, but has to be changed manually on upgrade.";
$ResultsConfigurationPage = "Results page configuration";
$HideExpectedAnswer = "Hide expected answers column";
$HideTotalScore = "Hide total score";
$HideQuestionScore = "Hide question score";
$SaveAnswers = "Save answers";
$SaveAllAnswers = "Pre-fill with answers from previous attempt";
$UserXReportedPostXInForumX = 'L\'utilisateur %s a signalé le message %s du forum %s'; $UserXReportedPostXInForumX = 'L\'utilisateur %s a signalé le message %s du forum %s';
?> ?>

@ -8233,4 +8233,122 @@ $DifferenceOfDaysAndCalendar = "Différence entre jours et calendrier";
$SkillUserList = "Liste des compétences et utilisateurs"; $SkillUserList = "Liste des compétences et utilisateurs";
$CourseId = "ID de cours"; $CourseId = "ID de cours";
$CareDetailView = "Vue détaillée de vie étudiante"; $CareDetailView = "Vue détaillée de vie étudiante";
$MoreDataAvailableInTheDatabaseButTrunkedForEfficiencyReasons = "Plus de données disponibles mais tronquées pour des raisons d'efficacité.";
$SendAnnouncementCopyToMyself = "M'envoyer une copie par e-mail.";
$CourseHoursDuration = "Durée du cours (h)";
$AnnouncementWillBeSentTo = "L'annonce sera envoyée à";
$FrmReadOutTextIntro = "Il est nécessaire d'ajouter un fichier audio correspondant au texte, en cliquant sur l'icône %s.";
$CreateReadOutText = "Créer texte lu";
$OutstandingStudents = "Apprenants distingués";
$PercentileScoresDistribution = "Distribution des scores en pourcents";
$ProgressObtainedFromLPProgressAndTestsAverage = "Note: Le progrès ci-dessus est obtenu par une combinaison du progrès dans les parcours et du score dans les exercices";
$CreateNewSurveyDoodle = "Créer une nouvelle enquête de type Doodle";
$RemoveMultiplicateQuestions = "Supprimer les questions démultipliées";
$MultiplicateQuestions = "Multiplier les questions";
$QuestionTags = "Vous pouvez utiliser les balises {{class_name}} et {{student_full_name}} dans la question afin de multiplier les questions.
Sur la page de liste d'enquêtes dans la colonne d'action, vous avez un bouton pour multiplier les questions qui recherchera la balise {{class_name}} et dupliquera la question pour toutes les classes inscrites au cours et la renommera avec le nom de la classe.
Il ajoutera également un séparateur de page pour créer une nouvelle page pour chaque classe.
Ensuite, il recherchera la balise {{student_full_name}} et dupliquera la question pour tous les étudiants de la classe (pour chaque classe) et la renommera avec le nom complet de l'étudiant.";
$CreateMeeting = "Créer un sondage de réunion";
$QuestionForNextClass = "Questions pour la classe suivante";
$NewExerciseAttemptDisabled = "Le portail ne permet pas le démarrage d'exercice pour le moment, revenez un peu plus tard pour essayer à nouveau";
$PersonalDataOfficer = "Délégué à la protection des données personnelles";
$MaxNumberSubscribedStudentsReached = "Le nombre maximum d'apprenants a déjà été atteint, il n'est pas possible d'en inscrire de nouveau";
$UserXAddedToCourseX = "L'utilisateur %s a été inscrit au cours %s";
$LpMinTime = "Temps minimum (minutes)";
$LpMinTimeDescription = "Temps minimum (en minutes) pendant lequel l'apprenant doit rester dans le parcours d'apprentissage pour pouvoir accéder au suivant.";
$LpMinTimeWarning = "Vous n'avez pas atteint le temps minimum dans le parcours.";
$YouHaveSpentXTime = "Vous avez dépensé %s";
$TimeSpentInLp = "Temps passé dans le parcours";
$StudentXFinishedLp = "L'apprenant %s a terminé ses parcours.";
$IHaveFinishedTheLessonsNotifyTheTeacher = "J'ai terminé les parcours du cours. Prévenir l'enseignant.";
$TheStudentXHasFinishedTheCourseYLearningPaths = "L'apprenant <b>%s</b> a terminé les parcours du cours <b>%s</b>";
$ThisQuestionExistsInAnotherExercisesWarning = "Cette question est utilisée dans d'autres exercices. Si vous l'éditez, les modifications affecteront tous les exercices qui contiennent cette question.";
$TimeSpentTimeRequired = "Temps passé / Temps requis";
$ProgressSpentInLp = "Progrès contenus";
$InSessionXYouHadTheFollowingResults = "Dans la formation %s, vous avez obtenu les résultats suivants:";
$TimeSpent = "Temps de connexion global";
$NumberOfVisits = "Nombre de passages";
$FormationUnit = "Unité de formation";
$GlobalProgress = "Progression Globale";
$AttestationOfAttendance = "Attestation d'assiduité";
$DontShowScoreOnlyWhenUserFinishesAllAttemptsButShowFeedbackEachAttempt = "Ne pas montrer le score (seulement quand l'utilisateur a utilisé toutes les tentatives) mais montrer la rétroalimentation pour chaque tentative.";
$ExerciseRankingMode = "Mode classement: Ne pas montrer les résultats question par question, mais montrer une table de classification par rapport aux autres utilisateurs.";
$BackToTop = "Sommet de la page";
$PersonalDataTreatmentTitleHelp = "Que considérons-nous comme des données personnelles?";
$PersonalDataCollectionTitleHelp = "Pourquoi recueillons-nous ces données?";
$PersonalDataRecordingTitleHelp = "Où enregistrons-nous les données? (dans quel système, hébergé où, accessible par quelle entreprise qui gère les serveurs, etc.)";
$PersonalDataOrganizationTitleHelp = "Comment sont structurées les données? Dans quelle mesure l'organisation des données protège-t-elle votre sécurité? ... Avons-nous mis en place des processus garantissant qu'un pirate informatique ne sera pas en mesure de collecter toutes les données personnelles à la première tentative? Sont-elles cryptées?";
$PersonalDataStructureTitleHelp = "Comment les données sont-elles structurées dans l'application? (un onglet pour toutes les données, il y a une catégorisation / étiquette, ...)";
$PersonalDataConservationTitleHelp = "Combien de temps sauvegardons-nous les données? Y a-t-il des données qui expirent même si le compte est toujours actif? Combien de temps les gardons-nous après la dernière utilisation du système?";
$PersonalDataAdaptationTitleHelp = "Quels changements pouvons-nous apporter aux données? Quelles modifications peuvent être apportées aux données sans affecter le service?";
$PersonalDataExtractionTitleHelp = "Pourquoi y-a-il extraction de données (vers d'autres processus internes) et quelles sont ces données?";
$PersonalDataConsultationTitleHelp = "Qui peut consulter les données personnelles, dans quel but?";
$PersonalDataUsageTitleHelp = "Comment et pourquoi pouvons-nous utiliser les données personnelles?";
$PersonalDataCommunicationTitleHelp = "Avec qui pouvons-nous les partager, dans quelles opportunités, à travers quels processus (sécurisés ou non)?";
$PersonalDataInterconnectionTitleHelp = "Avons-nous un autre système avec lequel Chamilo interagit? Quel est le processus d'interconnexion? Quelles sont les données échangées et de quelle manière?";
$PersonalDataLimitationTitleHelp = "Quelles sont les limites à respecter lors de l'utilisation de données personnelles? Jusqu'où pouvons-nous aller?";
$PersonalDataDeletionTitleHelp = "Après combien de temps effaçons-nous les données? (événement, dernière utilisation, validité du contrat, etc.) Quels sont les processus d'élimination?";
$PersonalDataDestructionTitleHelp = "Que se passe-t-il si les données sont détruites à la suite d'une défaillance technique? (suppression non autorisée ou perte de matériel, par exemple)";
$PersonalDataProfilingTitleHelp = "Dans quel but traitons-nous les données personnelles? Modifions-nous l'accès des utilisateurs à certaines parties de notre application? (discrimination négative ou positive)";
$SearchUsersByName = "Par nom";
$GlobalChat = "Chat";
$AskRevision = "Demander une révision";
$FeatureDisabledBecauseOfUnmaintainedThirdPartyLibraries = "Cette fonctionnalité a été désactivée en raison de l'interruption du support de librairies tierces nécessaires à son fonctionnement.";
$SearchUserByGeolocalization = "Par géolocalisation";
$GiveRevision = "Proposer une révision";
$ForumPostReported = "Signalement d'un message de forum";
$UserXReportedPostXInForumX = "L'utilisateur %s a signalé le message %s du forum %s";
$MyCommunities = "Mes communautés";
$SeeAllCommunities = "Voir toutes mes communautés";
$HideForumNotifications = "Cacher les notifications de forum";
$Forum_categoryFields = "Champs de catégorie de forum";
$Forum_postFields = "Champs de post de forum";
$ExerciseShowOnlyGlobalScoreAndCorrectAnswers = "Afficher uniquement le score global (pas celui de chaque question) et uniquement la réponse correcte (pas les réponses incorrectes)";
$Reported = "Signalé";
$FromTimeX = "De %s";
$ToTimeX = "à %s";
$SurveyInviteLink = "Lien d'invitation à l'enquête";
$RevisionProposed = "Révision proposée";
$SessionsPlanCalendar = "Calendrier de planification des sessions";
$QuizQuestionsLimitPerDay = "Limite de questions par jour";
$QuizQuestionsLimitPerDayComment = "Si ce paramètre est configuré à plus de zéro, il empêchera l'utilisateur d'entrer dans un exercice qui a plus de questions que ce qui lui reste. Par exemple, si la limite est de 50 et que l'utilisateur a déjà passé 2 exercices de 20 questions, le système ne permettra pas à l'utilisateur de commencer un 3ème exercice de 20 questions (20+20+20=60 > 50). Il pourra cependant encore commencer un exercice de 10 questions (20+20+10=50).";
$QuizQuestionsLimitPerDayXReached = "Désolé, vous avez atteint la quantité maximale de questions (%s) par jour. Réessayez demain.";
$VoteLike = "J'aime";
$VoteDislike = "Je n'aime pas";
$GenerateStats = "Générer les statistiques";
$BasicCourseDocuments = "Documents de base du cours";
$WeekX = "Semaine %s";
$QuizWrongAnswerHereIsTheCorrectOne = "Vous vous êtes trompé. La réponse correcte était :";
$ThisQuizCanBeEmbeddable = "Cet exercice peut être inséré dans une vidéo ou du contenu mobile";
$SearchGeolocalization = "Localiser par l'adresse";
$LegalTermsAgreementStatus = "État d'acceptation des conditions d'utilisation";
$HideSurveyInvitationLink = "Cacher le lien d'invitation à l'enquête";
$LeaveAMessage = "Laisser un message";
$OrphanQuestion = "Question orpheline";
$WelcomeToPortalXInCourseSessionX = "Bienvenue sur le portail %s, Cours/session: %s";
$WelcomeToPortalXInCourseSessionXCoursePartOfCareerX = "Bienvenue sur le portail %s dans le cours %s qui fait partie de la carrière %s";
$YourNextModule = "Votre module suivant";
$FirstLesson = "Première leçon";
$ImportCourseTeachersAsCourseCoach = "Importer les professeurs du cours comme tuteur du cours dans la session";
$ResumeImport = "Continuer l'import";
$Candidate = "Candidat";
$GeneralTotal = "Total général";
$Domains = "Domaines";
$ScormStartAttemptDate = "Date";
$LoginsByDate = "Logins par date";
$AllowHtaccessScormImport = "Permettre les fichiers htaccess dans les imports SCORM";
$ExerciseAutoEvaluationAndRankingMode = "Mode auto-évaluation et ranking: Montrer seulement la note globale (pas le détail de chaque question) et montrer la réponse correcte et la réponse sélectionnée par l'apprenant, tout en montrant la table de classification";
$YouAreReceivingACopyBecauseYouAreACourseCoach = "Vous recevez cette copie en tant que tuteur de ce cours";
$GenerateReport = "Générer le rapport";
$VersionFromVersionFile = "Version du fichier de version";
$VersionFromConfigFile = "Version du fichier de configuration";
$TheVersionFromTheVersionFileIsUpdatedWithEachVersionIfMainInstallDirectoryIsPresent = "La version du fichier version.php est mise à jour à chaque nouvelle version installée, mais n'est disponible que si le répertoire main/install/ est présent sur le disque.";
$TheVersionFromTheConfigurationFileShowsOnTheAdminPageButHasToBeChangedManuallyOnUpgrade = "La version du fichier de configuration principal s'affiche sur la page principale d'administration, mais doit être modifiée manuellement à chaque mise à jour.";
$ResultsConfigurationPage = "Configuration de la page de résultats";
$HideExpectedAnswer = "Cacher la colonne des choix attendus";
$HideTotalScore = "Cacher le score total";
$HideQuestionScore = "Cacher le score de chaque question";
$SaveAnswers = "Garder les réponses";
$SaveAllAnswers = "Préremplir avec les réponses de la tentative précédente";
?> ?>

@ -670,6 +670,11 @@ echo '<a href="'.api_get_self().'?'.Security::remove_XSS($_SERVER['QUERY_STRING'
echo '<a href="'.api_get_self().'?'.Security::remove_XSS($_SERVER['QUERY_STRING']).'&export=xls">' echo '<a href="'.api_get_self().'?'.Security::remove_XSS($_SERVER['QUERY_STRING']).'&export=xls">'
.Display::return_icon('export_excel.png', get_lang('ExportAsXLS'), '', ICON_SIZE_MEDIUM).'</a> '; .Display::return_icon('export_excel.png', get_lang('ExportAsXLS'), '', ICON_SIZE_MEDIUM).'</a> ';
echo Display::url(
Display::return_icon('export.png', get_lang('Export'), '', ICON_SIZE_MEDIUM),
api_get_path(WEB_CODE_PATH).'mySpace/access_details_session.php?user_id='.$student_id
);
if (!empty($user_info['email'])) { if (!empty($user_info['email'])) {
$send_mail = '<a href="mailto:'.$user_info['email'].'">'. $send_mail = '<a href="mailto:'.$user_info['email'].'">'.
Display::return_icon('mail_send.png', get_lang('SendMail'), '', ICON_SIZE_MEDIUM).'</a>'; Display::return_icon('mail_send.png', get_lang('SendMail'), '', ICON_SIZE_MEDIUM).'</a>';
@ -740,7 +745,18 @@ if (user_is_online($student_id)) {
// get average of score and average of progress by student // get average of score and average of progress by student
$avg_student_progress = $avg_student_score = 0; $avg_student_progress = $avg_student_score = 0;
if (CourseManager::is_user_subscribed_in_course($user_info['user_id'], $course_code, true)) { if (empty($sessionId)) {
$isSubscribedToCourse = CourseManager::is_user_subscribed_in_course($user_info['user_id'], $course_code);
} else {
$isSubscribedToCourse = CourseManager::is_user_subscribed_in_course(
$user_info['user_id'],
$course_code,
true,
$sessionId
);
}
if ($isSubscribedToCourse) {
$avg_student_progress = Tracking::get_avg_student_progress( $avg_student_progress = Tracking::get_avg_student_progress(
$user_info['user_id'], $user_info['user_id'],
$course_code, $course_code,

@ -222,7 +222,7 @@ jsPlumb.ready(function () {
} }
}); });
$(document).on('ready', function () { $(function () {
DraggableAnswer.init( DraggableAnswer.init(
$(".exercise-draggable-answer"), $(".exercise-draggable-answer"),
$(".droppable") $(".droppable")

@ -152,52 +152,77 @@
{# end left zone #} {# end left zone #}
{% endif %} {% endif %}
<div id="lp_navigation_elem" class="navegation-bar"> {# Right zone #}
{% if show_left_column == 1 %}
<a href="#" title = "{{ 'Expand'|get_lang }}" id="lp-view-expand-toggle" class="icon-toolbar expand" role="button">
{% if lp_mode == 'embedframe' %}
<span class="fa fa-compress" aria-hidden="true"></span>
<span class="sr-only">{{ 'Expand'|get_lang }}</span>
{% else %}
<span class="fa fa-expand" aria-hidden="true"></span>
<span class="sr-only">{{ 'Expand'|get_lang }}</span>
{% endif %}
</a>
{% endif %}
<a id="home-course" title = "{{ 'Home'|get_lang }}" href="{{ button_home_url }}" class="icon-toolbar" target="_self" onclick="javascript: window.parent.API.save_asset();">
<em class="fa fa-home"></em> <span class="hidden-xs hidden-sm"></span>
</a>
{{ navigation_bar }}
</div>
{# right zone #}
<div id="learning_path_right_zone" class="{{ show_left_column == 1 ? 'content-scorm' : 'no-right-col' }}"> <div id="learning_path_right_zone" class="{{ show_left_column == 1 ? 'content-scorm' : 'no-right-col' }}">
<div class="lp-view-zone-container"> <div class="lp-view-zone-container">
<div class="lp-view-tabs"> <div class="lp-view-tabs">
<div id="navTabsbar" class="nav-tabs-bar"> <div id="navTabsbar" class="nav-tabs-bar">
<ul id="navTabs" class="nav nav-tabs" role="tablist"> <ul id="navTabs" class="nav nav-tabs tabs-right" role="tablist">
<li role="presentation" class="active"> <li role="presentation" class="active">
<a href="#lp-view-content" title="{{ 'Lesson'|get_lang }}" aria-controls="lp-view-content" role="tab" data-toggle="tab"> <a href="#lp-view-content" title="{{ 'Lesson'|get_lang }}"
aria-controls="lp-view-content" role="tab" data-toggle="tab">
<span class="fa fa-book fa-2x fa-fw" aria-hidden="true"></span> <span class="fa fa-book fa-2x fa-fw" aria-hidden="true"></span>
<span class="sr-only">{{ 'Lesson'|get_lang }}</span> <span class="sr-only">{{ 'Lesson'|get_lang }}</span>
</a> </a>
</li> </li>
<li role="presentation"> <li role="presentation">
<a href="#lp-view-forum" title="{{ 'Forum'|get_lang }}" aria-controls="lp-view-forum" role="tab" data-toggle="tab"> <a href="#lp-view-forum" title="{{ 'Forum'|get_lang }}"
aria-controls="lp-view-forum" role="tab" data-toggle="tab">
<span class="fa fa-commenting-o fa-2x fa-fw" aria-hidden="true"></span> <span class="fa fa-commenting-o fa-2x fa-fw" aria-hidden="true"></span>
<span class="sr-only">{{ 'Forum'|get_lang }}</span> <span class="sr-only">{{ 'Forum'|get_lang }}</span>
</a> </a>
</li> </li>
</ul> </ul>
</div> </div>
<div class="tab-content"> <nav id="btn-menu-float" class="circular-menu">
<div class="circle">
{% if show_left_column == 1 %}
<a href="#" title = "{{ 'Expand'|get_lang }}" id="lp-view-expand-toggle"
class="icon-toolbar expand" role="button">
{% if lp_mode == 'embedframe' %}
<span class="fa fa-compress" aria-hidden="true"></span>
<span class="sr-only">{{ 'Expand'|get_lang }}</span>
{% else %}
<span class="fa fa-expand" aria-hidden="true"></span>
<span class="sr-only">{{ 'Expand'|get_lang }}</span>
{% endif %}
</a>
{% endif %}
<a id="home-course"
title = "{{ 'Home'|get_lang }}"
href="{{ button_home_url }}"
class="icon-toolbar" target="_self"
onclick="javascript: window.parent.API.save_asset();">
<em class="fa fa-home"></em> <span class="hidden-xs hidden-sm"></span>
</a>
{{ navigation_bar }}
</div>
<a class="menu-button fa fa-bars icons" href="#"></a>
</nav>
<div id="tab-iframe" class="tab-content">
<div role="tabpanel" class="tab-pane active" id="lp-view-content"> <div role="tabpanel" class="tab-pane active" id="lp-view-content">
<div id="wrapper-iframe"> <div id="wrapper-iframe">
{% if lp_mode == 'fullscreen' %} {% if lp_mode == 'fullscreen' %}
<iframe id="content_id_blank" name="content_name_blank" src="blank.php" style="width:100%; height:100%" border="0" frameborder="0" allowfullscreen="true" webkitallowfullscreen="true" mozallowfullscreen="true"></iframe> <iframe
id="content_id_blank"
name="content_name_blank"
src="blank.php"
style="width:100%; height:100%"
border="0"
frameborder="0"
allowfullscreen="true"
webkitallowfullscreen="true" mozallowfullscreen="true"></iframe>
{% else %} {% else %}
<iframe id="content_id" name="content_name" src="{{ iframe_src }}" style="width:100%; height:100%" border="0" frameborder="0" allowfullscreen="true" webkitallowfullscreen="true" mozallowfullscreen="true"></iframe> <iframe
id="content_id"
name="content_name"
src="{{ iframe_src }}"
style="width:100%; height:100%"
border="0"
frameborder="0"
allowfullscreen="true"
webkitallowfullscreen="true" mozallowfullscreen="true"></iframe>
{% endif %} {% endif %}
</div> </div>
</div> </div>
@ -211,7 +236,9 @@
</div> </div>
<script> <script>
(function () { document.querySelector('.menu-button').onclick = function(e) {
e.preventDefault(); document.querySelector('.circle').classList.toggle('open');
}
var LPViewUtils = { var LPViewUtils = {
setHeightLPToc: function () { setHeightLPToc: function () {
var scormInfoHeight = $('#scorm-info').outerHeight(true); var scormInfoHeight = $('#scorm-info').outerHeight(true);
@ -225,26 +252,19 @@
if (/iPhone|iPod|iPad/.test(navigator.userAgent)) { if (/iPhone|iPod|iPad/.test(navigator.userAgent)) {
// Fix an issue where you cannot scroll below first screen in // Fix an issue where you cannot scroll below first screen in
// learning paths on Apple devices // learning paths on Apple devices
document.getElementById('wrapper-iframe') document.getElementById('wrapper-iframe').setAttribute(
.setAttribute(
'style', 'style',
'width:100%; overflow:auto; position:auto; -webkit-overflow-scrolling:touch !important;' 'width:100%; overflow:auto; position:auto; -webkit-overflow-scrolling:touch !important;'
); );
// Fix another issue whereby buttons do not react to click below // Fix another issue whereby buttons do not react to click below
// second screen in learning paths on Apple devices // second screen in learning paths on Apple devices
document.getElementById('content_id') document.getElementById('content_id').setAttribute('style', 'overflow: auto;');
.setAttribute(
'style',
'overflow: auto;'
);
} }
{% if lp_mode == 'embedframe' %} {% if lp_mode == 'embedframe' %}
//$('#learning_path_main').addClass('lp-view-collapsed'); //$('#learning_path_main').addClass('lp-view-collapsed');
$('#lp-view-expand-button, #lp-view-expand-toggle').on('click', function (e) { $('#lp-view-expand-button, #lp-view-expand-toggle').on('click', function (e) {
e.preventDefault(); e.preventDefault();
$('#learning_path_main').toggleClass('lp-view-collapsed'); $('#learning_path_main').toggleClass('lp-view-collapsed');
$('#lp-view-expand-toggle span.fa').toggleClass('fa-compress'); $('#lp-view-expand-toggle span.fa').toggleClass('fa-compress');
$('#lp-view-expand-toggle span.fa').toggleClass('fa-expand'); $('#lp-view-expand-toggle span.fa').toggleClass('fa-expand');
@ -255,19 +275,16 @@
$(this).attr('title', '{{ "Collapse" | get_lang }}'); $(this).attr('title', '{{ "Collapse" | get_lang }}');
} }
if($('#navTabsbar').is(':hidden')){ if($('#navTabsbar').is(':hidden')) {
$('#navTabsbar').show(); $('#navTabsbar').show();
} else { } else {
$('#navTabsbar').hide(); $('#navTabsbar').hide();
} }
}); });
{% else %} {% else %}
$('#lp-view-expand-button, #lp-view-expand-toggle').on('click', function (e) { $('#lp-view-expand-button, #lp-view-expand-toggle').on('click', function (e) {
e.preventDefault(); e.preventDefault();
$('#learning_path_main').toggleClass('lp-view-collapsed'); $('#learning_path_main').toggleClass('lp-view-collapsed');
$('#lp-view-expand-toggle span.fa').toggleClass('fa-expand'); $('#lp-view-expand-toggle span.fa').toggleClass('fa-expand');
$('#lp-view-expand-toggle span.fa').toggleClass('fa-compress'); $('#lp-view-expand-toggle span.fa').toggleClass('fa-compress');
@ -277,12 +294,6 @@
} else { } else {
$(this).attr('title', '{{ "Collapse" | get_lang }}'); $(this).attr('title', '{{ "Collapse" | get_lang }}');
} }
if($('#navTabsbar').is(':hidden')){
$('#navTabsbar').show();
} else {
$('#navTabsbar').hide();
}
}); });
{% endif %} {% endif %}
@ -303,6 +314,10 @@
LPViewUtils.setHeightLPToc(); LPViewUtils.setHeightLPToc();
$('.image-avatar img').load(function () {
LPViewUtils.setHeightLPToc();
});
$('.scorm_item_normal a, #scorm-previous, #scorm-next').on('click', function () { $('.scorm_item_normal a, #scorm-previous, #scorm-next').on('click', function () {
$('.lp-view-tabs').animate({opacity: 0}, 500); $('.lp-view-tabs').animate({opacity: 0}, 500);
}); });
@ -323,65 +338,60 @@
function(){ function(){
// $("<div>I am a div courses</div>").prependTo("body"); // $("<div>I am a div courses</div>").prependTo("body");
}, },
"top.content_name", "#content_id",
{ [
load: [ { type:"script", id:"_fr1", src:"{{ jquery_web_path }}", deps: [
{ type:"script", id:"_fr1", src:"{{ jquery_web_path }}"},
{ type:"script", id:"_fr4", src:"{{ jquery_ui_js_web_path }}"}, { type:"script", id:"_fr4", src:"{{ jquery_ui_js_web_path }}"},
{ type:"stylesheet", id:"_fr5", src:"{{ jquery_ui_css_web_path }}"},
{ type:"script", id:"_fr2", src:"{{ _p.web_lib }}javascript/jquery.highlight.js"}, { type:"script", id:"_fr2", src:"{{ _p.web_lib }}javascript/jquery.highlight.js"},
{{ fix_link }} {{ fix_link }}
]},
{ type:"stylesheet", id:"_fr5", src:"{{ jquery_ui_css_web_path }}"},
] ]
}
); );
{% elseif show_glossary_in_documents == 'isautomatic' %} {% elseif show_glossary_in_documents == 'isautomatic' %}
$.frameReady( $.frameReady(
function(){ function(){
// $("<div>I am a div courses</div>").prependTo("body"); // $("<div>I am a div courses</div>").prependTo("body");
}, },
"top.content_name", "#content_id",
{ [
load: [ { type:"script", id:"_fr1", src:"{{ jquery_web_path }}", deps: [
{ type:"script", id:"_fr1", src:"{{ jquery_web_path }}"},
{ type:"script", id:"_fr4", src:"{{ jquery_ui_js_web_path }}"}, { type:"script", id:"_fr4", src:"{{ jquery_ui_js_web_path }}"},
{ type:"stylesheet", id:"_fr5", src:"{{ jquery_ui_css_web_path }}"},
{ type:"script", id:"_fr2", src:"{{ _p.web_lib }}javascript/jquery.highlight.js"}, { type:"script", id:"_fr2", src:"{{ _p.web_lib }}javascript/jquery.highlight.js"},
{{ fix_link }} {{ fix_link }}
]},
{ type:"stylesheet", id:"_fr5", src:"{{ jquery_ui_css_web_path }}"},
] ]
}
); );
{% elseif fix_link != '' %} {% elseif fix_link != '' %}
$.frameReady( $.frameReady(
function(){ function(){
// $("<div>I am a div courses</div>").prependTo("body"); // $("<div>I am a div courses</div>").prependTo("body");
}, },
"top.content_name", "#content_id",
{ [
load: [ { type:"script", id:"_fr1", src:"{{ jquery_web_path }}", deps: [
{ type:"script", id:"_fr1", src:"{{ jquery_web_path }}"},
{ type:"script", id:"_fr4", src:"{{ jquery_ui_js_web_path }}"}, { type:"script", id:"_fr4", src:"{{ jquery_ui_js_web_path }}"},
{ type:"stylesheet", id:"_fr5", src:"{{ jquery_ui_css_web_path }}"},
{{ fix_link }} {{ fix_link }}
]},
{ type:"stylesheet", id:"_fr5", src:"{{ jquery_ui_css_web_path }}"},
] ]
}
); );
{% endif %} {% endif %}
})(); })();
{% endif %} {% endif %}
{% if disable_js_in_lp_view == 0 %} {% if disable_js_in_lp_view == 0 %}
$(function() { $(function() {
$('iframe#content_id').on('load', function () {
var arr = ['link', 'sco']; var arr = ['link', 'sco'];
if ($.inArray(olms.lms_item_type, arr) == -1) { if ($.inArray(olms.lms_item_type, arr) == -1) {
{{ frame_ready }} {{ frame_ready }}
} }
}); });
});
{% endif %} {% endif %}
$(window).on('resize', function () { $(window).on('resize', function () {
LPViewUtils.setHeightLPToc(); LPViewUtils.setHeightLPToc();
}); });
}); });
})();
</script> </script>

@ -0,0 +1,278 @@
<?php
/* For licensing terms, see /license.txt */
use Chamilo\CoreBundle\Entity\TrackEAttempt;
/**
* Class QuestionOptionsEvaluationPlugin.
*/
class QuestionOptionsEvaluationPlugin extends Plugin
{
const SETTING_ENABLE = 'enable';
const SETTING_MAX_SCORE = 'exercise_max_score';
const EXTRAFIELD_FORMULA = 'quiz_evaluation_formula';
/**
* QuestionValuationPlugin constructor.
*/
protected function __construct()
{
$version = '1.0';
$author = 'Angel Fernando Quiroz Campos';
parent::__construct(
$version,
$author,
[
self::SETTING_ENABLE => 'boolean',
self::SETTING_MAX_SCORE => 'text',
]
);
}
/**
* @return QuestionOptionsEvaluationPlugin|null
*/
public static function create()
{
static $result = null;
return $result ? $result : $result = new self();
}
/**
* @param int $exerciseId
* @param int $iconSize
*
* @return string
*/
public static function filterModify($exerciseId, $iconSize = ICON_SIZE_SMALL)
{
$directory = basename(__DIR__);
$title = get_plugin_lang('plugin_title', self::class);
$enabled = api_get_plugin_setting('questionoptionsevaluation', 'enable');
if ('true' !== $enabled) {
return '';
}
return Display::url(
Display::return_icon('options_evaluation.png', $title, [], $iconSize),
api_get_path(WEB_PATH)."plugin/$directory/evaluation.php?exercise=$exerciseId",
[
'class' => 'ajax',
'data-size' => 'md',
'data-title' => get_plugin_lang('plugin_title', self::class),
]
);
}
public function install()
{
$this->createExtraField();
}
public function uninstall()
{
$this->removeExtraField();
}
/**
* @return Plugin
*/
public function performActionsAfterConfigure()
{
return $this;
}
/**
* @param int $formula
* @param Exercise $exercise
*/
public function saveFormulaForExercise($formula, Exercise $exercise)
{
$this->recalculateQuestionScore($formula, $exercise);
$extraFieldValue = new ExtraFieldValue('quiz');
$extraFieldValue->save(
[
'item_id' => $exercise->iId,
'variable' => self::EXTRAFIELD_FORMULA,
'value' => $formula,
]
);
}
/**
* @param int $exerciseId
*
* @return int
*/
public function getFormulaForExercise($exerciseId)
{
$extraFieldValue = new ExtraFieldValue('quiz');
$value = $extraFieldValue->get_values_by_handler_and_field_variable(
$exerciseId,
self::EXTRAFIELD_FORMULA
);
if (empty($value)) {
return 0;
}
return (int) $value['value'];
}
/**
* @return int
*/
public function getMaxScore()
{
$max = $this->get(self::SETTING_MAX_SCORE);
if (!empty($max)) {
return (int) $max;
}
return 10;
}
/**
* @param int $trackId
* @param int $formula
*
* @throws \Doctrine\ORM\ORMException
* @throws \Doctrine\ORM\OptimisticLockException
* @throws \Doctrine\ORM\TransactionRequiredException
*
* @return float|int
*/
public function getResultWithFormula($trackId, $formula)
{
$em = Database::getManager();
$eTrack = $em->find('ChamiloCoreBundle:TrackEExercises', $trackId);
$qTracks = $em
->createQuery(
'SELECT a FROM ChamiloCoreBundle:TrackEAttempt a
WHERE a.exeId = :id AND a.userId = :user AND a.cId = :course AND a.sessionId = :session'
)
->setParameters(
[
'id' => $eTrack->getExeId(),
'course' => $eTrack->getCId(),
'session' => $eTrack->getSessionId(),
'user' => $eTrack->getExeUserId(),
]
)
->getResult();
$counts = ['correct' => 0, 'incorrect' => 0];
/** @var TrackEAttempt $qTrack */
foreach ($qTracks as $qTrack) {
if ($qTrack->getMarks() > 0) {
$counts['correct']++;
} elseif ($qTrack->getMarks() < 0) {
$counts['incorrect']++;
}
}
switch ($formula) {
case 1:
$result = $counts['correct'] - $counts['incorrect'];
break;
case 2:
$result = $counts['correct'] - $counts['incorrect'] / 2;
break;
case 3:
$result = $counts['correct'] - $counts['incorrect'] / 3;
break;
}
$score = ($result / count($qTracks)) * $this->getMaxScore();
return $score >= 0 ? $score : 0;
}
/**
* @param int $formula
* @param Exercise $exercise
*/
private function recalculateQuestionScore($formula, Exercise $exercise)
{
$tblQuestion = Database::get_course_table(TABLE_QUIZ_QUESTION);
$tblAnswer = Database::get_course_table(TABLE_QUIZ_ANSWER);
foreach ($exercise->questionList as $questionId) {
$question = Question::read($questionId, $exercise->course, false);
if (!in_array($question->selectType(), [UNIQUE_ANSWER, MULTIPLE_ANSWER])) {
continue;
}
$questionAnswers = new Answer($questionId, $exercise->course_id, $exercise);
$counts = array_count_values($questionAnswers->correct);
$questionPonderation = 0;
foreach ($questionAnswers->correct as $i => $isCorrect) {
if (!isset($questionAnswers->iid[$i])) {
continue;
}
$iid = $questionAnswers->iid[$i];
if ($question->selectType() == MULTIPLE_ANSWER || 0 === $formula) {
$ponderation = 1 == $isCorrect ? 1 / $counts[1] : -1 / $counts[0];
} else {
$ponderation = 1 == $isCorrect ? 1 : -1 / $formula;
}
if ($ponderation > 0) {
$questionPonderation += $ponderation;
}
//error_log("question: $questionId -- i: $i -- w: $ponderation");
Database::query("UPDATE $tblAnswer SET ponderation = $ponderation WHERE iid = $iid");
}
Database::query("UPDATE $tblQuestion SET ponderation = $questionPonderation WHERE iid = {$question->iid}");
}
}
/**
* Creates an extrafield.
*/
private function createExtraField()
{
$extraField = new ExtraField('quiz');
if (false === $extraField->get_handler_field_info_by_field_variable(self::EXTRAFIELD_FORMULA)) {
$extraField
->save(
[
'variable' => self::EXTRAFIELD_FORMULA,
'field_type' => ExtraField::FIELD_TYPE_TEXT,
'display_text' => $this->get_lang('EvaluationFormula'),
'visible_to_self' => false,
'changeable' => false,
]
);
}
}
/**
* Removes the extrafield .
*/
private function removeExtraField()
{
$extraField = new ExtraField('quiz');
$value = $extraField->get_handler_field_info_by_field_variable(self::EXTRAFIELD_FORMULA);
if (false !== $value) {
$extraField->delete($value['id']);
}
}
}

@ -0,0 +1,20 @@
# Questions Options Evaluation
Allow recalulate the options score in questions:
* Successes - Failures
* Successes - Failures / 2
* Successes - Failures / 3
* Recalculate question scores
**Setup instructions**
- Install plugin
- Set enabled in configuration
- Edit `configuration.php` file
```php
$_configuration['exercise_additional_teacher_modify_actions'] = [
// ...
'questionoptionsevaluation' => ['QuestionOptionsEvaluationPlugin', 'filterModify']
];
```

@ -0,0 +1,74 @@
<?php
/* For licensing terms, see /license.txt */
require_once __DIR__.'/../../main/inc/global.inc.php';
api_protect_teacher_script();
api_protect_course_script();
$exerciseId = isset($_REQUEST['exercise']) ? (int) $_REQUEST['exercise'] : 0;
if (empty($exerciseId)) {
echo Display::return_message(get_lang('NotAllowed'), 'error');
exit;
}
$exercise = new Exercise();
if (!$exercise->read($exerciseId, false)) {
echo Display::return_message(get_lang('ExerciseNotFound'), 'error');
exit;
}
$plugin = QuestionOptionsEvaluationPlugin::create();
if ($plugin->get('enable') !== 'true') {
echo Display::return_message(get_lang('NotAllowed'), 'error');
exit;
}
$formEvaluation = new FormValidator('evaluation');
$formEvaluation
->addRadio(
'formula',
$plugin->get_lang('EvaluationFormula'),
[
-1 => $plugin->get_lang('NoFormula'),
0 => $plugin->get_lang('RecalculateQuestionScores'),
1 => $plugin->get_lang('Formula1'),
2 => $plugin->get_lang('Formula2'),
3 => $plugin->get_lang('Formula3'),
]
)
->setColumnsSize([4, 7, 1]);
$formEvaluation->addButtonSave(get_lang('Save'))->setColumnsSize([4, 7, 1]);
$formEvaluation->addHidden('exercise', $exerciseId);
if ($formEvaluation->validate()) {
$exercise->read($exerciseId, true);
$values = $formEvaluation->exportValues();
$formula = isset($values['formula']) ? (int) $values['formula'] : 0;
$plugin->saveFormulaForExercise($formula, $exercise);
Display::addFlash(
Display::return_message(
sprintf($plugin->get_lang('FormulaSavedForExerciseX'), $exercise->selectTitle(true)),
'success'
)
);
header(
'Location: '.api_get_path(WEB_CODE_PATH).'exercise/exercise.php?'.api_get_cidreq()."&exerciseId=$exerciseId"
);
exit;
}
$formEvaluation->setDefaults(['formula' => $plugin->getFormulaForExercise($exercise->iId)]);
echo Display::return_message(
$plugin->get_lang('QuizQuestionsScoreRulesTitleConfirm'),
'warning'
);
$formEvaluation->display();

@ -0,0 +1,4 @@
<?php
/* For licensing terms, see /license.txt */
QuestionOptionsEvaluationPlugin::create()->install();

@ -0,0 +1,19 @@
<?php
/* For licensing terms, see /license.txt */
$strings['plugin_title'] = 'Question options evaluation';
$strings['plugin_comment'] = 'Allow recalulate the options score in questions';
$strings['enable'] = 'Enable';
$strings['exercise_max_score'] = 'Max score in exercises';
$strings['exercise_max_score_help'] = 'Default is 10.';
$strings['QuizQuestionsScoreRulesTitleConfirm'] = 'Changing the evaluation formula generates changes for each question in the exercise and prevents undoing this change below. Are you sure you want to proceed?';
$strings['EvaluationFormula'] = 'Evaluation formula';
$strings['NoFormula'] = 'No formula';
$strings['Formula1'] = 'Successes - Failures';
$strings['Formula2'] = 'Successes - Failures / 2';
$strings['Formula3'] = 'Successes - Failures / 3';
$strings['QuestionsEvaluated'] = 'Questions evaluated';
$strings['RecalculateQuestionScores'] = 'Recalculate question scores';
$strings['FormulaSavedForExerciseX'] = 'Formula saved for exercise "%s".';

@ -0,0 +1,19 @@
<?php
/* For licensing terms, see /license.txt */
$strings['plugin_title'] = 'Evaluación para opciones de pregunta';
$strings['plugin_comment'] = 'Permite recalcular los puntajes de opciones en preguntas';
$strings['enable'] = 'Habilitar';
$strings['exercise_max_score'] = 'Puntuación máxima por ejercicio';
$strings['exercise_max_score_help'] = 'Por defecto es 10.';
$strings['QuizQuestionsScoreRulesTitleConfirm'] = 'Cambiar la fórmula de evaluación genera cambios para cada pregunta del ejercicio e impide deshacer este cambio a continuación. ¿Está seguro que desea proceder?';
$strings['EvaluationFormula'] = 'Fórmula de evaluación';
$strings['NoFormula'] = 'Sin formula';
$strings['Formula1'] = 'Aciertos - Fallos';
$strings['Formula2'] = 'Aciertos - Fallos / 2';
$strings['Formula3'] = 'Aciertos - Fallos / 3';
$strings['QuestionsEvaluated'] = 'Preguntas evaluadas';
$strings['RecalculateQuestionScores'] = 'Recalcular puntuaciones de preguntas';
$strings['FormulaSavedForExerciseX'] = 'Formula guardada para el ejercicio "%s".';

@ -0,0 +1,4 @@
<?php
/* For licensing terms, see /license.txt */
$plugin_info = QuestionOptionsEvaluationPlugin::create()->get_info();

@ -0,0 +1,4 @@
<?php
/* For licensing terms, see /license.txt */
QuestionOptionsEvaluationPlugin::create()->uninstall();

@ -0,0 +1,42 @@
# Survey Export CSV
Exports survey results to a CSV file with a very specific format.
This plugin will add a new action button in the surveys list, allowing the
teacher to export the survey in a CSV format meant at exchanging with external
analysis tools.
The CSV format looks this way:
```
DATID;P01;P02;P03;P04;P05;P06;P07;P08;DATOBS
1;"1";"2";"26";"10";"2";"2";"2";"4";"2"
2;"1";"2";"32";"10";"6";"4";"4";"5";"2"
3;"2";"3";"27";"8";"5";"5";"2";"5";"1"
4;"1";"3";"33";"11";"1";"4";"1";"6";"1"
```
Where:
- DATID represents a sequential ID for the participants (not related to
their internal user ID)
- P01,P02,... represent the sequential ID of each question inside the survey
- DATOBS represents the free answer of the user to an open remarks form at
the end of the survey
**Setup instructions**
- Install plugin
- Set enabled in configuration
- Edit `configuration.php` file
```php
$_configuration['survey_additional_teacher_modify_actions'] = [
// ...
'SurveyExportCSVPlugin' => ['SurveyExportCsvPlugin', 'filterModify'],
];
```
If you have large surveys with large numbers of users answering them, you
might want to ensure your c_survey_answer table is properly indexed. If not,
use the following SQL statement to modify that:
```sql
alter table c_survey_answer add index idx_c_survey_answerucsq (user, c_id, survey_id, question_id);
```

@ -0,0 +1,95 @@
<?php
/* For licensing terms, see /license.txt */
/**
* Class SurveyExportCsvPlugin.
*/
class SurveyExportCsvPlugin extends Plugin
{
/**
* SurveyExportCsvPlugin constructor.
*/
protected function __construct()
{
$settings = [
'enabled' => 'boolean',
'export_incomplete' => 'boolean',
];
parent::__construct('0.1', 'Angel Fernando Quiroz Campos', $settings);
}
/**
* @return SurveyExportCsvPlugin|null
*/
public static function create()
{
static $result = null;
return $result ? $result : $result = new self();
}
/**
* Installation process.
*/
public function install()
{
}
/**
* Uninstallation process.
*/
public function uninstall()
{
}
/**
* @param array $params
*
* @return string
*/
public static function filterModify($params)
{
$enabled = api_get_plugin_setting('surveyexportcsv', 'enabled');
if ($enabled !== 'true') {
return '';
}
$surveyId = isset($params['survey_id']) ? (int) $params['survey_id'] : 0;
$iconSize = isset($params['icon_size']) ? $params['icon_size'] : ICON_SIZE_SMALL;
if (empty($surveyId)) {
return '';
}
return Display::url(
Display::return_icon('export_csv.png', get_lang('ExportAsCSV'), [], $iconSize),
api_get_path(WEB_PLUGIN_PATH).'surveyexportcsv/export.php?survey='.$surveyId.'&'.api_get_cidreq()
);
}
/**
* Create tools for all courses.
*/
private function createLinkToCourseTools()
{
$result = Database::getManager()
->createQuery('SELECT c.id FROM ChamiloCoreBundle:Course c')
->getResult();
foreach ($result as $item) {
$this->createLinkToCourseTool($this->get_name().':teacher', $item['id'], 'survey.png');
}
}
/**
* Remove all course tools created by plugin.
*/
private function removeLinkToCourseTools()
{
Database::getManager()
->createQuery('DELETE FROM ChamiloCourseBundle:CTool t WHERE t.link LIKE :link AND t.category = :category')
->execute(['link' => 'surveyexportcsv/start.php%', 'category' => 'plugin']);
}
}

@ -0,0 +1,215 @@
<?php
/* For licensing terms, see /license.txt */
use Chamilo\CourseBundle\Entity\CSurveyAnswer;
use Chamilo\CourseBundle\Entity\CSurveyQuestionOption;
require_once __DIR__.'/../../main/inc/global.inc.php';
api_protect_course_script(true);
api_protect_teacher_script();
$surveyId = isset($_GET['survey']) ? (int) $_GET['survey'] : 0;
$surveyData = SurveyManager::get_survey($surveyId);
$courseId = api_get_course_int_id();
if (empty($surveyData)) {
api_not_allowed(true);
}
$plugin = SurveyExportCsvPlugin::create();
$allowExportIncomplete = 'true' === $plugin->get('export_incomplete');
if ($plugin->get('enabled') !== 'true') {
api_not_allowed(true);
}
$questionsData = SurveyManager::get_questions($surveyId, $courseId);
// Sort questions by their "sort" field
$questionsData = array_filter(
$questionsData,
function ($questionData) {
return in_array($questionData['type'], ['yesno', 'multiplechoice', 'open']);
}
);
$numberOfQuestions = count($questionsData);
usort(
$questionsData,
function ($qL, $qR) {
if ($qL['sort'] == $qR['sort']) {
return 0;
}
return $qL['sort'] < $qR['sort'] ? -1 : 1;
}
);
$content = [];
$content[] = firstRow($questionsData);
$surveyAnswers = getSurveyAnswers($courseId, $surveyId);
// Process answers
$i = 1;
foreach ($surveyAnswers as $answer) {
$row = otherRow($questionsData, $answer['user'], $courseId);
if (!$allowExportIncomplete && count($row) < $numberOfQuestions) {
continue;
}
array_unshift($row, $i);
$content[] = $row;
$i++;
}
// Generate file
$fileName = md5($surveyId.time());
Export::arrayToCsv($content, $fileName, false, "'");
/**
* Generate the first row for file.
*
* @param $questions
*
* @return array
*/
function firstRow($questions)
{
array_pop($questions);
$positions = array_keys($questions);
$row = ['DATID'];
foreach ($positions as $position) {
$row[] = sprintf("P%02d", $position + 1);
}
$row[] = 'DATOBS';
return $row;
}
/**
* Get unique answer for surveys by users.
*
* @param int $courseId
* @param int $surveyId
*
* @return array
*/
function getSurveyAnswers($courseId, $surveyId)
{
$surveyAnswers = Database::getManager()
->createQuery(
'SELECT sa.user, MIN(sa.iid) AS id FROM ChamiloCourseBundle:CSurveyAnswer sa
WHERE sa.cId = :course AND sa.surveyId = :survey
GROUP BY sa.user ORDER BY id ASC'
)
->setParameters(['course' => $courseId, 'survey' => $surveyId])
->getResult();
return $surveyAnswers;
}
/**
* @param string $user
* @param int $courseId
* @param int $surveyId
* @param int $questionId
*
* @return array
*/
function getQuestionOptions($user, $courseId, $surveyId, $questionId)
{
$options = Database::getManager()
->createQuery(
'SELECT sqo FROM ChamiloCourseBundle:CSurveyQuestionOption sqo
INNER JOIN ChamiloCourseBundle:CSurveyAnswer sa
WITH
sqo.cId = sa.cId
AND sqo.questionId = sa.questionId
AND sqo.surveyId = sa.surveyId
AND sqo.iid = sa.optionId
WHERE sa.user = :user AND sa.cId = :course AND sa.surveyId = :survey AND sa.questionId = :question'
)
->setParameters(
[
'user' => $user,
'course' => $courseId,
'survey' => $surveyId,
'question' => $questionId,
]
)
->getResult();
return $options;
}
/**
* @param int $questionId
* @param int $surveyId
* @param int $courseId
* @param string $user
*
* @throws \Doctrine\ORM\NonUniqueResultException
*
* @return CSurveyAnswer|null
*/
function getOpenAnswer($questionId, $surveyId, $courseId, $user)
{
$answer = Database::getManager()
->createQuery(
'SELECT sa FROM ChamiloCourseBundle:CSurveyAnswer sa
WHERE sa.cId = :course AND sa.surveyId = :survey AND sa.questionId = :question AND sa.user = :user'
)
->setParameters(['course' => $courseId, 'survey' => $surveyId, 'question' => $questionId, 'user' => $user])
->getOneOrNullResult();
return $answer;
}
/**
* Generate the content rows for file.
*
* @param array $questions
* @param string $user
* @param int $courseId
*
* @throws \Doctrine\ORM\NonUniqueResultException
*
* @return array
*/
function otherRow($questions, $user, $courseId)
{
$row = [];
foreach ($questions as $question) {
if ('open' === $question['type']) {
$answer = getOpenAnswer($question['question_id'], $question['survey_id'], $courseId, $user);
if ($answer) {
$row[] = Security::remove_XSS($answer->getOptionId());
}
} else {
$options = getQuestionOptions(
$user,
$courseId,
$question['survey_id'],
$question['question_id']
);
/** @var CSurveyQuestionOption|null $option */
$option = end($options);
if ($option) {
$value = $option->getSort();
$row[] = '"'.$value.'"';
}
}
}
return $row;
}

@ -0,0 +1,4 @@
<?php
/* For licensing terms, see /license.txt */
SurveyExportCsvPlugin::create()->install();

@ -0,0 +1,9 @@
<?php
/* For licensing terms, see /license.txt */
$strings['plugin_title'] = "Survey Export CSV";
$strings['plugin_comment'] = "Export surveys results to CSV file";
$strings['enabled'] = 'Enabled';
$strings['export_incomplete'] = 'Export incomplete';
$strings['export_incomplete_help'] = 'Allow export incomplete surveys. Disabled by default.';

@ -0,0 +1,9 @@
<?php
/* For licensing terms, see /license.txt */
$strings['plugin_title'] = "Survey Export CSV";
$strings['plugin_comment'] = "Exportar resultados de encuestas en archivo CSV.";
$strings['enabled'] = 'Habilitado';
$strings['export_incomplete'] = 'Exportar incompletos';
$strings['export_incomplete_help'] = 'Permitir exportar encuestas incompletas. Deshabilitado por defecto.';

@ -0,0 +1,4 @@
<?php
/* For licensing terms, see /license.txt */
$plugin_info = SurveyExportCsvPlugin::create()->get_info();

@ -0,0 +1,50 @@
<?php
/* For licensing terms, see /license.txt */
require_once __DIR__.'/../../main/inc/global.inc.php';
api_protect_course_script(true);
api_protect_teacher_script();
$plugin = SurveyExportCsvPlugin::create();
$courseCode = api_get_course_id();
// Create a sortable table with survey-data
$table = new SortableTable(
'surveys',
['SurveyUtil', 'get_number_of_surveys'],
['SurveyUtil', 'get_survey_data'],
2
);
$table->set_additional_parameters(['cidReq' => $courseCode]);
$table->set_header(0, '', false);
$table->setHideColumn(0);
$table->set_header(1, get_lang('SurveyName'));
$table->set_header(2, get_lang('SurveyCode'), true, ['class' => 'text-center'], ['class' => 'text-center']);
$table->set_header(3, get_lang('NumberOfQuestions'), true, ['class' => 'text-right'], ['class' => 'text-right']);
$table->set_header(4, get_lang('Author'));
$table->set_header(5, get_lang('AvailableFrom'), true, ['class' => 'text-center'], ['class' => 'text-center']);
$table->set_header(6, get_lang('AvailableUntil'), true, ['class' => 'text-center'], ['class' => 'text-center']);
$table->set_header(7, get_lang('Invite'), true, ['class' => 'text-right'], ['class' => 'text-right']);
$table->set_header(8, get_lang('Anonymous'), true, ['class' => 'text-center'], ['class' => 'text-center']);
$table->set_column_filter(8, ['SurveyUtil', 'anonymous_filter']);
if (api_get_configuration_value('allow_mandatory_survey')) {
$table->set_header(9, get_lang('IsMandatory'), true, ['class' => 'text-center'], ['class' => 'text-center']);
$table->set_header(10, get_lang('Export'), false, ['class' => 'text-center'], ['class' => 'text-center']);
$table->set_column_filter(10, ['SurveyExportCsvPlugin', 'filterModify']);
} else {
$table->set_header(9, get_lang('Export'), false, ['class' => 'text-center'], ['class' => 'text-center']);
$table->set_column_filter(9, ['SurveyExportCsvPlugin', 'filterModify']);
}
$pageTitle = $plugin->get_title();
$template = new Template($pageTitle);
$content = $table->return_table();
$template->assign('header', $pageTitle);
$template->assign('content', $content);
$template->display_one_col_template();

@ -0,0 +1,4 @@
<?php
/* For licensing terms, see /license.txt */
SurveyExportCsvPlugin::create()->uninstall();

@ -0,0 +1,10 @@
Speech authentication with Whispeak
===================================
Instructions:
-------------
1. Install plugin in Chamilo.
2. Set the plugin configuration with the token and API url. And enable the plugin.
3. Set the `login_bottom` region to the plugin.
4. Add `$_configuration['whispeak_auth_enabled'] = true;` to `configuration.php` file.

@ -0,0 +1,293 @@
<?php
/* For licensing terms, see /license.txt */
use Chamilo\CoreBundle\Entity\ExtraField;
use Chamilo\CoreBundle\Entity\ExtraFieldValues;
use Chamilo\UserBundle\Entity\User;
/**
* Class WhispeakAuthPlugin.
*/
class WhispeakAuthPlugin extends Plugin
{
const SETTING_ENABLE = 'enable';
const SETTING_API_URL = 'api_url';
const SETTING_TOKEN = 'token';
const SETTING_INSTRUCTION = 'instruction';
const EXTRAFIELD_AUTH_UID = 'whispeak_auth_uid';
/**
* StudentFollowUpPlugin constructor.
*/
protected function __construct()
{
parent::__construct(
'0.1',
'Angel Fernando Quiroz',
[
self::SETTING_ENABLE => 'boolean',
self::SETTING_API_URL => 'text',
self::SETTING_TOKEN => 'text',
self::SETTING_INSTRUCTION => 'html',
]
);
}
/**
* @return WhispeakAuthPlugin
*/
public static function create()
{
static $result = null;
return $result ? $result : $result = new self();
}
public function install()
{
UserManager::create_extra_field(
self::EXTRAFIELD_AUTH_UID,
\ExtraField::FIELD_TYPE_TEXT,
$this->get_lang('Whispeak uid'),
''
);
}
public function uninstall()
{
$extraField = self::getAuthUidExtraField();
if (empty($extraField)) {
return;
}
$em = Database::getManager();
$em->createQuery('DELETE FROM ChamiloCoreBundle:ExtraFieldValues efv WHERE efv.field = :field')
->execute(['field' => $extraField]);
$em->remove($extraField);
$em->flush();
}
/**
* @return ExtraField
*/
public static function getAuthUidExtraField()
{
$em = Database::getManager();
$efRepo = $em->getRepository('ChamiloCoreBundle:ExtraField');
/** @var ExtraField $extraField */
$extraField = $efRepo->findOneBy(
[
'variable' => self::EXTRAFIELD_AUTH_UID,
'extraFieldType' => ExtraField::USER_FIELD_TYPE,
]
);
return $extraField;
}
/**
* @param int $userId
*
* @return ExtraFieldValues
*/
public static function getAuthUidValue($userId)
{
$extraField = self::getAuthUidExtraField();
$em = Database::getManager();
$efvRepo = $em->getRepository('ChamiloCoreBundle:ExtraFieldValues');
/** @var ExtraFieldValues $value */
$value = $efvRepo->findOneBy(['field' => $extraField, 'itemId' => $userId]);
return $value;
}
/**
* @param int $userId
*
* @return bool
*/
public static function checkUserIsEnrolled($userId)
{
$value = self::getAuthUidValue($userId);
if (empty($value)) {
return false;
}
return !empty($value->getValue());
}
/**
* @return string
*/
public static function getEnrollmentUrl()
{
return api_get_path(WEB_PLUGIN_PATH).'whispeakauth/enrollment.php';
}
/**
* @param User $user
* @param string $filePath
*
* @return array
*/
public function requestEnrollment(User $user, $filePath)
{
$metadata = [
'motherTongue' => $user->getLanguage(),
'spokenTongue' => $user->getLanguage(),
'audioType' => 'pcm',
];
return $this->sendRequest(
'enrollment',
$metadata,
$user,
$filePath
);
}
/**
* @param User $user
* @param string $uid
*
* @throws \Doctrine\ORM\OptimisticLockException
*/
public function saveEnrollment(User $user, $uid)
{
$em = Database::getManager();
$value = self::getAuthUidValue($user->getId());
if (empty($value)) {
$ef = self::getAuthUidExtraField();
$now = new DateTime('now', new DateTimeZone('UTC'));
$value = new ExtraFieldValues();
$value
->setField($ef)
->setItemId($user->getId())
->setUpdatedAt($now);
}
$value->setValue($uid);
$em->persist($value);
$em->flush();
}
public function requestAuthentify(User $user, $filePath)
{
$value = self::getAuthUidValue($user->getId());
if (empty($value)) {
return null;
}
$metadata = [
'uid' => $value->getValue(),
'audioType' => 'pcm',
];
return $this->sendRequest(
'authentify',
$metadata,
$user,
$filePath
);
}
/**
* @return string
*/
public function getAuthentifySampleText()
{
$phrases = [];
for ($i = 1; $i <= 6; $i++) {
$phrases[] = $this->get_lang("AuthentifySampleText$i");
}
$rand = array_rand($phrases, 1);
return $phrases[$rand];
}
/**
* @return bool
*/
public function toolIsEnabled()
{
return 'true' === $this->get(self::SETTING_ENABLE);
}
/**
* Access not allowed when tool is not enabled.
*
* @param bool $printHeaders Optional. Print headers.
*/
public function protectTool($printHeaders = true)
{
if ($this->toolIsEnabled()) {
return;
}
api_not_allowed($printHeaders);
}
/**
* @return string
*/
private function getApiUrl()
{
$url = $this->get(self::SETTING_API_URL);
return trim($url, " \t\n\r \v/");
}
/**
* @param string $endPoint
* @param array $metadata
* @param User $user
* @param string $filePath
*
* @return array
*/
private function sendRequest($endPoint, array $metadata, User $user, $filePath)
{
$moderator = $user->getCreatorId() ?: $user->getId();
$apiUrl = $this->getApiUrl()."/$endPoint";
$headers = [
//"Content-Type: application/x-www-form-urlencoded",
"Authorization: Bearer ".$this->get(self::SETTING_TOKEN),
];
$post = [
'metadata' => json_encode($metadata),
'moderator' => "moderator_$moderator",
'client' => base64_encode($user->getUserId()),
'voice' => new CURLFile($filePath),
];
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $apiUrl);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$result = curl_exec($ch);
curl_close($ch);
$result = json_decode($result, true);
if (!empty($result['error'])) {
return null;
}
return json_decode($result, true);
}
}

@ -0,0 +1,136 @@
<?php
/* For licensing terms, see /license.txt */
use Chamilo\UserBundle\Entity\User;
use FFMpeg\FFMpeg;
use FFMpeg\Format\Audio\Wav;
$cidReset = true;
require_once __DIR__.'/../../../main/inc/global.inc.php';
$action = isset($_POST['action']) ? $_POST['action'] : 'enrollment';
$isEnrollment = 'enrollment' === $action;
$isAuthentify = 'authentify' === $action;
$isAllowed = true;
if ($isEnrollment) {
api_block_anonymous_users(false);
$isAllowed = !empty($_FILES['audio']);
} elseif ($isAuthentify) {
$isAllowed = !empty($_POST['username']) && !empty($_FILES['audio']);
}
if (!$isAllowed) {
echo Display::return_message(get_lang('NotAllowed'), 'error');
exit;
}
$plugin = WhispeakAuthPlugin::create();
$plugin->protectTool(false);
if ($isAuthentify) {
$em = Database::getManager();
/** @var User|null $user */
$user = $em->getRepository('ChamiloUserBundle:User')->findOneBy(['username' => $_POST['username']]);
} else {
/** @var User $user */
$user = api_get_user_entity(api_get_user_id());
}
if (empty($user)) {
echo Display::return_message(get_lang('NoUser'), 'error');
exit;
}
$path = api_upload_file('whispeakauth', $_FILES['audio'], $user->getId());
if (false === $path) {
echo Display::return_message(get_lang('UploadError'), 'error');
exit;
}
$newFullPath = $originFullPath = api_get_path(SYS_UPLOAD_PATH).'whispeakauth'.$path['path_to_save'];
$fileType = mime_content_type($originFullPath);
if ('wav' !== substr($fileType, -3)) {
$directory = dirname($originFullPath);
$newFullPath = $directory.'/audio.wav';
try {
$ffmpeg = FFMpeg::create();
$audio = $ffmpeg->open($originFullPath);
$audio->save(new Wav(), $newFullPath);
} catch (Exception $exception) {
echo Display::return_message($exception->getMessage(), 'error');
exit;
}
}
if ($isEnrollment) {
$result = $plugin->requestEnrollment($user, $newFullPath);
if (empty($result)) {
echo Display::return_message($plugin->get_lang('EnrollmentFailed'));
exit;
}
$reliability = (int) $result['reliability'];
if ($reliability <= 0) {
echo Display::return_message($plugin->get_lang('EnrollmentSignature0'), 'error');
exit;
}
$plugin->saveEnrollment($user, $result['uid']);
$message = '<strong>'.$plugin->get_lang('EnrollmentSuccess').'</strong>';
$message .= PHP_EOL;
$message .= $plugin->get_lang("EnrollmentSignature$reliability");
echo Display::return_message($message, 'success', false);
exit;
}
if ($isAuthentify) {
$result = $plugin->requestAuthentify($user, $newFullPath);
if (empty($result)) {
echo Display::return_message($plugin->get_lang('AuthentifyFailed'), 'error');
exit;
}
$success = (bool) $result['audio'][0]['result'];
if (!$success) {
echo Display::return_message($plugin->get_lang('TryAgain'), 'warning');
exit;
}
$loggedUser = [
'user_id' => $user->getId(),
'status' => $user->getStatus(),
'uidReset' => true,
];
ChamiloSession::write('_user', $loggedUser);
Login::init_user($user->getId(), true);
echo Display::return_message($plugin->get_lang('AuthentifySuccess'), 'success');
echo '<script>window.location.href = "'.api_get_path(WEB_PATH).'";</script>';
exit;
}

@ -0,0 +1,139 @@
/* For licensing terms, see /license.txt */
window.RecordAudio = (function () {
function useRecordRTC(rtcInfo) {
$(rtcInfo.blockId).show();
var mediaConstraints = {audio: true},
localStream = null,
recordRTC = null,
btnStart = $(rtcInfo.btnStartId),
btnStop = $(rtcInfo.btnStopId),
btnSave = $(rtcInfo.btnSaveId),
tagAudio = $(rtcInfo.plyrPreviewId);
function saveAudio() {
var recordedBlob = recordRTC.getBlob();
if (!recordedBlob) {
return;
}
var btnSaveText = btnSave.html();
var fileExtension = recordedBlob.type.split('/')[1];
var formData = new FormData();
formData.append('audio', recordedBlob, 'audio.' + fileExtension);
for (var prop in rtcInfo.data) {
if (!rtcInfo.data.hasOwnProperty(prop)) {
continue;
}
formData.append(prop, rtcInfo.data[prop]);
}
$.ajax({
url: _p.web_plugin + 'whispeakauth/ajax/record_audio.php',
data: formData,
processData: false,
contentType: false,
type: 'POST',
beforeSend: function () {
btnStart.prop('disabled', true);
btnStop.prop('disabled', true);
btnSave.prop('disabled', true).text(btnSave.data('loadingtext'));
}
}).done(function (response) {
$('#messages-deck').append(response);
}).always(function () {
btnSave.prop('disabled', true).html(btnSaveText).parent().addClass('hidden');
btnStop.prop('disabled', true).parent().addClass('hidden');
btnStart.prop('disabled', false).parent().removeClass('hidden');
});
}
btnStart.on('click', function () {
tagAudio.prop('src', '');
function successCallback(stream) {
localStream = stream;
recordRTC = RecordRTC(stream, {
recorderType: StereoAudioRecorder,
numberOfAudioChannels: 1,
type: 'audio'
});
recordRTC.startRecording();
btnSave.prop('disabled', true).parent().addClass('hidden');
btnStop.prop('disabled', false).parent().removeClass('hidden');
btnStart.prop('disabled', true).parent().addClass('hidden');
tagAudio.removeClass('show').parents('#audio-wrapper').addClass('hidden');
}
function errorCallback(error) {
alert(error.message);
}
if (navigator.mediaDevices.getUserMedia) {
navigator.mediaDevices.getUserMedia(mediaConstraints)
.then(successCallback)
.catch(errorCallback);
return;
}
navigator.getUserMedia = navigator.getUserMedia || navigator.mozGetUserMedia || navigator.webkitGetUserMedia;
if (navigator.getUserMedia) {
navigator.getUserMedia(mediaConstraints, successCallback, errorCallback);
}
});
btnStop.on('click', function () {
if (!recordRTC) {
return;
}
recordRTC.stopRecording(function (audioURL) {
btnStart.prop('disabled', false).parent().removeClass('hidden');
btnStop.prop('disabled', true).parent().addClass('hidden');
btnSave.prop('disabled', false).parent().removeClass('hidden');
tagAudio
.prop('src', audioURL)
.parents('#audio-wrapper')
.removeClass('hidden')
.addClass('show');
localStream.getTracks()[0].stop();
});
});
btnSave.on('click', function () {
if (!recordRTC) {
return;
}
saveAudio();
});
}
return {
init: function (rtcInfo) {
$(rtcInfo.blockId).hide();
var userMediaEnabled = (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) ||
!!navigator.webkitGetUserMedia ||
!!navigator.mozGetUserMedia ||
!!navigator.getUserMedia;
if (!userMediaEnabled) {
return;
}
useRecordRTC(rtcInfo);
}
}
})();

@ -0,0 +1,26 @@
<?php
/* For licensing terms, see /license.txt */
$cidReset = true;
require_once __DIR__.'/../../main/inc/global.inc.php';
$plugin = WhispeakAuthPlugin::create();
$plugin->protectTool();
$form = new FormValidator('enter_username', 'post', '#');
$form->addText('username', get_lang('Username'));
$htmlHeadXtra[] = api_get_js('rtc/RecordRTC.js');
$htmlHeadXtra[] = api_get_js_simple(api_get_path(WEB_PLUGIN_PATH).'whispeakauth/assets/js/RecordAudio.js');
$template = new Template();
$template->assign('form', $form->returnForm());
$template->assign('sample_text', $plugin->getAuthentifySampleText());
$content = $template->fetch('whispeakauth/view/authentify_recorder.html.twig');
$template->assign('header', $plugin->get_title());
$template->assign('content', $content);
$template->display_one_col_template();

@ -0,0 +1,28 @@
<?php
/* For licensing terms, see /license.txt */
$cidReset = true;
require_once __DIR__.'/../../main/inc/global.inc.php';
api_block_anonymous_users(true);
$userId = api_get_user_id();
$plugin = WhispeakAuthPlugin::create();
$plugin->protectTool();
$sampleText = $plugin->get_lang('EnrollmentSampleText');
$htmlHeadXtra[] = api_get_js('rtc/RecordRTC.js');
$htmlHeadXtra[] = api_get_js_simple(api_get_path(WEB_PLUGIN_PATH).'whispeakauth/assets/js/RecordAudio.js');
$template = new Template();
$template->assign('is_authenticated', WhispeakAuthPlugin::checkUserIsEnrolled($userId));
$template->assign('sample_text', $sampleText);
$content = $template->fetch('whispeakauth/view/record_audio.html.twig');
$template->assign('header', $plugin->get_title());
$template->assign('content', $content);
$template->display_one_col_template();

@ -0,0 +1,14 @@
<?php
/* For licensing terms, see /license.txt */
$plugin = WhispeakAuthPlugin::create();
if ($plugin->toolIsEnabled()) {
echo Display::toolbarButton(
$plugin->get_lang('SpeechAuthentication'),
api_get_path(WEB_PLUGIN_PATH).'whispeakauth/authentify.php',
'sign-in',
'info',
['class' => 'btn-block']
);
}

@ -0,0 +1,4 @@
<?php
/* For licensing terms, see /license.txt */
WhispeakAuthPlugin::create()->install();

@ -0,0 +1,33 @@
<?php
/* For licensing terms, see /license.txt */
$strings['plugin_title'] = 'Speech authentication with Whispeak';
$strings['plugin_comment'] = 'Allow speech authentication in Chamilo.';
$strings['enable'] = 'Enable';
$strings['api_url'] = 'API URL';
$strings['api_url_help'] = 'http://api.whispeak.io:8080/v1/';
$strings['token'] = 'API key';
$strings['instruction'] = '<p>Add <code>$_configuration[\'whispeak_auth_enabled\'] = true;</code>'.
'in the <code>configuration.php</code> file</p>';
$strings['EnrollmentSampleText'] = 'The famous Mona Lisa painting was painted by Leonardo Da Vinci.';
$strings['AuthentifySampleText1'] = 'Dropping Like Flies.';
$strings['AuthentifySampleText2'] = 'Keep Your Eyes Peeled.';
$strings['AuthentifySampleText3'] = 'The fox screams at midnight.';
$strings['AuthentifySampleText4'] = 'Go Out On a Limb.';
$strings['AuthentifySampleText5'] = 'Under the Water.';
$strings['AuthentifySampleText6'] = 'Barking Up The Wrong Tree.';
$strings['RepeatThisPhrase'] = 'Repeat this phrase three times after allowing audio recording:';
$strings['EnrollmentSignature0'] = 'Unsustainable signature requires a new enrollment.';
$strings['EnrollmentSignature1'] = 'Passable signature, advice to make a new enrollment.';
$strings['EnrollmentSignature2'] = 'Correct signature.';
$strings['EnrollmentSignature3'] = 'Good signature.';
$strings['SpeechAuthAlreadyEnrolled'] = 'Speech authentication already enrolled previously.';
$strings['SpeechAuthentication'] = 'Speech authentication';
$strings['EnrollmentFailed'] = 'Enrollment failed.';
$strings['EnrollmentSuccess'] = 'Enrollment success.';
$strings['AuthentifyFailed'] = 'Login failed.';
$strings['AuthentifySuccess'] = 'Authentication success!';
$strings['TryAgain'] = 'Try again';

@ -0,0 +1,18 @@
<?php
/* For licensing terms, see /license.txt */
$strings['plugin_title'] = 'Authentification vocale avec Whispeak';
$strings['EnrollmentSampleText'] = 'Le fameux chef-d\'oeuvre Mona Lisa a été peint par Léonardo da Vinci.';
$strings['RepeatThisPhrase'] = 'Autorisez l\'enregistrement audio puis répétez cette phrase trois fois:';
$strings['EnrollmentSignature0'] = 'Signature non viable, nécessite un nouvel enrôlement';
$strings['EnrollmentSignature1'] = 'Signature passable, conseil de faire un nouvel enrôlement.';
$strings['EnrollmentSignature2'] = 'Signature correcte.';
$strings['EnrollmentSignature3'] = 'Signature bonne.';
$strings['SpeechAuthentication'] = 'Authentification de voix';
$strings['EnrollmentFailed'] = 'Échec à l\'inscription.';
$strings['EnrollmentSuccess'] = 'Inscription réussie.';
$strings['AuthentifyFailed'] = 'Échec de l\'authentification.';
$strings['AuthentifySuccess'] = 'Authentification réussie!';
$strings['TryAgain'] = 'Essayez encore';

@ -0,0 +1,33 @@
<?php
/* For licensing terms, see /license.txt */
$strings['plugin_title'] = 'Authenticación de voz con Whispeak';
$strings['plugin_comment'] = 'Permitir autenticación de voz en Chamilo.';
$strings['enable'] = 'Habilitar';
$strings['api_url'] = 'URL del API';
$strings['api_url_help'] = 'http://api.whispeak.io:8080/v1/';
$strings['token'] = 'Llave del API';
$strings['instruction'] = '<p>Agrega <code>$_configuration[\'whispeak_auth_enabled\'] = true;</code>'.
'al archivo <code>configuration.php</code></p>';
$strings['EnrollmentSampleText'] = 'El famoso cuadro de Mona Lisa fue pintado por Leonardo Da Vinci.';
$strings['AuthentifySampleText1'] = 'Cayendo como moscas.';
$strings['AuthentifySampleText2'] = 'Mantén tus ojos abiertos.';
$strings['AuthentifySampleText3'] = 'El zorro grita a medianoche.';
$strings['AuthentifySampleText4'] = 'Ir por las ramas.';
$strings['AuthentifySampleText5'] = 'Debajo del agua.';
$strings['AuthentifySampleText6'] = 'Ladrando al árbol equivocado.';
$strings['RepeatThisPhrase'] = 'Repita esta frase tres veces después de permitir la grabación de audio:';
$strings['EnrollmentSignature0'] = 'Firma insostenible, requiere una nueva inscripción.';
$strings['EnrollmentSignature1'] = 'Firma aceptable, pero se aconseja hacer una nueva inscripción.';
$strings['EnrollmentSignature2'] = 'Firma correcta.';
$strings['EnrollmentSignature3'] = 'Buena firma.';
$strings['SpeechAuthAlreadyEnrolled'] = 'Autenticación de voz registrada anteriormente.';
$strings['SpeechAuthentication'] = 'Atenticación con voz';
$strings['EnrollmentFailed'] = 'Inscripción fallida.';
$strings['EnrollmentSuccess'] = 'Inscripción correcta.';
$strings['AuthentifyFailed'] = 'Inicio de sesión fallido.';
$strings['AuthentifySuccess'] = '¡Autenticación correcta!';
$strings['TryAgain'] = 'Intente de nuevo.';

@ -0,0 +1,4 @@
<?php
/* For licensing terms, see /license.txt */
$plugin_info = WhispeakAuthPlugin::create()->get_info();

@ -0,0 +1,4 @@
<?php
/* For licensing terms, see /license.txt */
WhispeakAuthPlugin::create()->uninstall();

@ -0,0 +1,38 @@
{% extends 'whispeakauth/view/record_audio.html.twig' %}
{% block intro %}
<form class="form-horizontal" action="#" method="post">
<div class="form-group ">
<label for="enter_username_username" class="col-sm-4 control-label">
{{ 'Username'|get_lang }}
</label>
<div class="col-sm-8">
<input class="form-control" name="username" type="text" id="username">
</div>
</div>
</form>
<hr>
{{ parent() }}
{% endblock %}
{% block config_data %}
$('#username').on('change', function () {
$('#record-audio-recordrtc, #btn-start-record, #btn-stop-record, #btn-save-record').off('click', '');
RecordAudio.init(
{
blockId: '#record-audio-recordrtc',
btnStartId: '#btn-start-record',
btnStopId: '#btn-stop-record',
btnSaveId: '#btn-save-record',
plyrPreviewId: '#record-preview',
data: {
action: 'authentify',
username: $('#username').val()
}
}
);
});
{% endblock %}

@ -0,0 +1,67 @@
<div id="record-audio-recordrtc" class="row">
<div class="col-sm-6">
{% block intro %}
<p class="text-center">{{ 'RepeatThisPhrase'|get_plugin_lang('WhispeakAuthPlugin') }}</p>
<div class="well well-sm">
<div class="row">
<div class="col-sm-3 text-center">
<span class="fa fa-microphone fa-5x fa-fw" aria-hidden="true"></span>
<span class="sr-only">{{ 'RecordAudio'|get_lang }}</span>
</div>
<div class="col-sm-9 text-center">
<p class="lead">{{ sample_text }}</p>
</div>
</div>
</div>
{% endblock %}
<ul class="list-inline text-center">
<li>
<button class="btn btn-primary" type="button" id="btn-start-record">
<span class="fa fa-circle fa-fw" aria-hidden="true"></span> {{ 'StartRecordingAudio'|get_lang }}
</button>
</li>
<li class="hidden">
<button class="btn btn-danger" type="button" id="btn-stop-record" disabled>
<span class="fa fa-square fa-fw" aria-hidden="true"></span> {{ 'StopRecordingAudio'|get_lang }}
</button>
</li>
<li class="hidden">
<button class="btn btn-success" type="button" id="btn-save-record"
data-loadingtext="{{ 'Uploading'|get_lang }}" disabled>
<span class="fa fa-send fa-fw" aria-hidden="true"></span> {{ 'SaveRecordedAudio'|get_lang }}
</button>
</li>
</ul>
<p class="hidden" id="audio-wrapper">
<audio class="center-block" controls id="record-preview"></audio>
</p>
</div>
<div class="col-sm-5 col-sm-offset-1" id="messages-deck">
{% if is_authenticated %}
<div class="alert alert-info">
<span class="fa fa-info-circle" aria-hidden="true"></span>
<strong>{{ 'SpeechAuthAlreadyEnrolled'|get_plugin_lang('WhispeakAuthPlugin') }}</strong>
</div>
{% endif %}
</div>
</div>
<script>
$(function () {
{% block config_data %}
RecordAudio.init(
{
blockId: '#record-audio-recordrtc',
btnStartId: '#btn-start-record',
btnStopId: '#btn-stop-record',
btnSaveId: '#btn-save-record',
plyrPreviewId: '#record-preview',
data: {
action: 'enrollment'
}
}
);
{% endblock %}
});
</script>

@ -21,7 +21,7 @@ class FeatureContext extends MinkContext
*/ */
public function iAmAPlatformAdministrator() public function iAmAPlatformAdministrator()
{ {
$this->visit('/index.php?logout=logout'); $this->visit('/index.php?logout=logout&uid=1');
$this->iAmOnHomepage(); $this->iAmOnHomepage();
$this->fillField('login', 'admin'); $this->fillField('login', 'admin');
$this->fillField('password', 'admin'); $this->fillField('password', 'admin');
@ -95,7 +95,7 @@ class FeatureContext extends MinkContext
public function courseExists($argument) public function courseExists($argument)
{ {
$this->iAmAPlatformAdministrator(); $this->iAmAPlatformAdministrator();
$this->visit('/main/admin/course_list.php?keyword=' . $argument); $this->visit('/main/admin/course_list.php?keyword='.$argument);
$this->assertPageContainsText($argument); $this->assertPageContainsText($argument);
} }
@ -105,7 +105,7 @@ class FeatureContext extends MinkContext
public function courseIsDeleted($argument) public function courseIsDeleted($argument)
{ {
$this->iAmAPlatformAdministrator(); $this->iAmAPlatformAdministrator();
$this->visit('/main/admin/course_list.php?keyword=' . $argument); $this->visit('/main/admin/course_list.php?keyword='.$argument);
$this->clickLink('Delete'); $this->clickLink('Delete');
} }
@ -142,10 +142,14 @@ class FeatureContext extends MinkContext
{ {
$this->visit('/index.php?logout=logout'); $this->visit('/index.php?logout=logout');
$this->iAmOnHomepage(); $this->iAmOnHomepage();
$this->fillFields(new TableNode([ $this->fillFields(
new TableNode(
[
['login', $username], ['login', $username],
['password', $username] ['password', $username],
])); ]
)
);
$this->pressButton('submitAuth'); $this->pressButton('submitAuth');
} }
@ -158,16 +162,22 @@ class FeatureContext extends MinkContext
$friendId = $friendId; $friendId = $friendId;
$friendUsername = $friendUsername; $friendUsername = $friendUsername;
$sendInvitationURL = '/main/inc/ajax/message.ajax.php?' . http_build_query([ $sendInvitationURL = '/main/inc/ajax/message.ajax.php?'.
http_build_query(
[
'a' => 'send_invitation', 'a' => 'send_invitation',
'user_id' => $friendId, 'user_id' => $friendId,
'content' => 'Add me' 'content' => 'Add me',
]); ]
$acceptInvitationURL = '/main/inc/ajax/social.ajax.php?' . http_build_query([ );
$acceptInvitationURL = '/main/inc/ajax/social.ajax.php?'.
http_build_query(
[
'a' => 'add_friend', 'a' => 'add_friend',
'friend_id' => $adminId, 'friend_id' => $adminId,
'is_my_friend' => 'friend' 'is_my_friend' => 'friend',
]); ]
);
$this->iAmAPlatformAdministrator(); $this->iAmAPlatformAdministrator();
$this->visit($sendInvitationURL); $this->visit($sendInvitationURL);
@ -182,13 +192,17 @@ class FeatureContext extends MinkContext
public function iHaveAPublicPasswordProtectedCourse($code, $password) public function iHaveAPublicPasswordProtectedCourse($code, $password)
{ {
$this->visit('/main/admin/course_add.php'); $this->visit('/main/admin/course_add.php');
$this->fillFields(new TableNode([ $this->fillFields(
new TableNode(
[
['title', 'Password Protected'], ['title', 'Password Protected'],
['visual_code', $code], ['visual_code', $code],
['visibility', 3] ['visibility', 3],
])); ]
)
);
$this->pressButton('submit'); $this->pressButton('submit');
$this->visit('/main/course_info/infocours.php?cidReq=' . $code); $this->visit('/main/course_info/infocours.php?cidReq='.$code);
$this->assertPageContainsText('Course registration password'); $this->assertPageContainsText('Course registration password');
$this->fillField('course_registration_password', $password); $this->fillField('course_registration_password', $password);
$this->pressButton('submit_save'); $this->pressButton('submit_save');
@ -209,7 +223,7 @@ class FeatureContext extends MinkContext
*/ */
public function iInviteAFriendToASocialGroup($friendId, $groupId) public function iInviteAFriendToASocialGroup($friendId, $groupId)
{ {
$this->visit('/main/social/group_invitation.php?id=' . $groupId); $this->visit('/main/social/group_invitation.php?id='.$groupId);
$this->fillField('invitation[]', $friendId); $this->fillField('invitation[]', $friendId);
$this->pressButton('submit'); $this->pressButton('submit');
} }
@ -248,7 +262,7 @@ class FeatureContext extends MinkContext
*/ */
public function iAmOnSocialGroupMembersPageWithId($groupId) public function iAmOnSocialGroupMembersPageWithId($groupId)
{ {
$this->visit('/main/social/group_view.php?id=' . $groupId); $this->visit('/main/social/group_view.php?id='.$groupId);
} }
/** /**
@ -256,11 +270,15 @@ class FeatureContext extends MinkContext
*/ */
public function iTryDeleteAFriendFromSocialGroup($friendId, $groupId) public function iTryDeleteAFriendFromSocialGroup($friendId, $groupId)
{ {
$this->visit('/main/social/group_members.php?' . http_build_query([ $this->visit(
'/main/social/group_members.php?'.http_build_query(
[
'id' => $groupId, 'id' => $groupId,
'u' => $friendId, 'u' => $friendId,
'action' => 'delete' 'action' => 'delete',
])); ]
)
);
} }
/** /**
@ -384,10 +402,18 @@ class FeatureContext extends MinkContext
*/ */
public function waitForThePageToBeLoaded() public function waitForThePageToBeLoaded()
{ {
//$this->getSession()->wait(10000, "document.readyState === 'complete'");
$this->getSession()->wait(3000); $this->getSession()->wait(3000);
} }
/**
* @When /^wait very long for the page to be loaded$/
*/
public function waitVeryLongForThePageToBeLoaded()
{
//$this->getSession()->wait(10000, "document.readyState === 'complete'");
$this->getSession()->wait(8000);
}
/** /**
* @When /^I check the "([^"]*)" radio button$/ * @When /^I check the "([^"]*)" radio button$/
*/ */
@ -473,28 +499,28 @@ class FeatureContext extends MinkContext
return false; return false;
} }
/** /**
* @Given /^I am a student subscribed to session "([^"]*)"$/ * @Then /^I save current URL with name "([^"]*)"$/
*
* @param string$sessionName
*/ */
public function iAmStudentSubscribedToXSession($sessionName) public function saveUrlWithName($name)
{ {
$this->iAmAPlatformAdministrator();
$this->visit('/main/session/session_add.php'); $url = $this->getSession()->getCurrentUrl();
$this->fillField('name', $sessionName); $this->getSession()->setCookie($name, $url);
$this->pressButton('Next step'); }
$this->selectOption('NoSessionCoursesList[]', 'TEMP (TEMP)');
$this->pressButton('add_course'); /**
$this->pressButton('Next step'); * @Then /^I visit URL saved with name "([^"]*)"$/
$this->assertPageContainsText('Update successful'); */
$this->fillField('user_to_add', 'acostea'); public function visitSavedUrlWithName($name)
$this->waitForThePageToBeLoaded(); {
$this->clickLink('Costea Andrea (acostea)'); $url = $this->getSession()->getCookie($name);
$this->pressButton('Finish session creation'); echo $url;
$this->assertPageContainsText('Session overview'); if (empty($url)) {
//$this->assertPageContainsText('Costea Andrea (acostea)'); throw new Exception("Url with name: $name not found");
$this->iAmAStudent(); }
$this->visit($url);
} }
/** /**

@ -7,7 +7,7 @@ Feature: Subscribe users to the course
Given I am on "/main/user/subscribe_user.php?keyword=amann&type=5&cidReq=TEMP" Given I am on "/main/user/subscribe_user.php?keyword=amann&type=5&cidReq=TEMP"
Then I should see "Aimee" Then I should see "Aimee"
Then I follow "Register" Then I follow "Register"
Then I should see "Aimee Mann (amann) has been registered to your course" Then I should see "User Aimee Mann (amann) has been registered to course TEMP"
Scenario: Unsubscribe user "amann" the course "TEMP" Scenario: Unsubscribe user "amann" the course "TEMP"
Given I am on "/main/user/user.php?cidReq=TEMP" Given I am on "/main/user/user.php?cidReq=TEMP"
@ -20,4 +20,16 @@ Feature: Subscribe users to the course
Given I am on "/main/user/subscribe_user.php?keyword=acostea&type=5&cidReq=TEMP" Given I am on "/main/user/subscribe_user.php?keyword=acostea&type=5&cidReq=TEMP"
Then I should see "Andrea" Then I should see "Andrea"
Then I follow "Register" Then I follow "Register"
Then I should see "Andrea Costea (acostea) has been registered to your course" Then I should see "User Andrea Costea (acostea) has been registered to course TEMP"
Scenario: Subscribe "fapple" as student to the course "TEMP" (leave it subscribed for further tests)
Given I am on "/main/user/subscribe_user.php?keyword=fapple&type=5&cidReq=TEMP"
Then I should see "Fiona"
Then I follow "Register"
Then I should see "User Fiona Apple Maggart (fapple) has been registered to course TEMP"
Scenario: Subscribe "amann" again as student to the course "TEMP" (leave it subscribed for further tests)
Given I am on "/main/user/subscribe_user.php?keyword=amann&type=5&cidReq=TEMP"
Then I should see "Aimee"
Then I follow "Register"
Then I should see "User Aimee Mann (amann) has been registered to course TEMP"

@ -31,8 +31,9 @@ Feature: Session management tool
Given I am on "/main/session/session_add.php" Given I am on "/main/session/session_add.php"
When I fill in the following: When I fill in the following:
| name | Temp Session | | name | Temp Session |
And I press advanced settings
And I fill in select2 input "#coach_username" with id "1" and value "admin" And I fill in select2 input "#coach_username" with id "1" and value "admin"
And I press "advanced_params" And wait for the page to be loaded
And I fill in ckeditor field "description" with "Description for Temp Session" And I fill in ckeditor field "description" with "Description for Temp Session"
And I press "submit" And I press "submit"
Then I should see "Add courses to this session (Temp Session)" Then I should see "Add courses to this session (Temp Session)"
@ -50,7 +51,7 @@ Feature: Session management tool
Given I am on "/main/session/session_list.php?keyword=Temp+session" Given I am on "/main/session/session_list.php?keyword=Temp+session"
And wait for the page to be loaded And wait for the page to be loaded
And I follow "Edit" And I follow "Edit"
When I press "advanced_params" And I press advanced settings
And I check "Show description" And I check "Show description"
And I press "submit" And I press "submit"
Then I should see "Update successful" Then I should see "Update successful"

@ -14,7 +14,10 @@ Feature: Announcement tool
And I select "John Doe" from "users" And I select "John Doe" from "users"
And I press "add" And I press "add"
And I fill in ckeditor field "content" with "Announcement description" And I fill in ckeditor field "content" with "Announcement description"
And I press "submit" And I follow "Preview"
And wait for the page to be loaded
Then I should see "Announcement will be sent to"
Then I press "submit"
Then I should see "Announcement has been added" Then I should see "Announcement has been added"
Scenario: Create an announcement for all users Scenario: Create an announcement for all users
@ -22,7 +25,10 @@ Feature: Announcement tool
When I fill in the following: When I fill in the following:
| title | Announcement test | | title | Announcement test |
And I fill in ckeditor field "content" with "Announcement description" And I fill in ckeditor field "content" with "Announcement description"
And I press "submit" And I follow "Preview"
And wait for the page to be loaded
Then I should see "Announcement will be sent to"
Then I press "submit"
Then I should see "Announcement has been added" Then I should see "Announcement has been added"
Scenario: Delete all announcements Scenario: Delete all announcements

@ -21,7 +21,7 @@ Feature: Document tool
And I press "Create the folder" And I press "Create the folder"
Then I should see "Unable to create the folder" Then I should see "Unable to create the folder"
Scenario: Create a HTML document Scenario: Create a simple document
Given I am on "/main/document/create_document.php?cidReq=TEMP" Given I am on "/main/document/create_document.php?cidReq=TEMP"
Then I should see "Create a rich media page / activity" Then I should see "Create a rich media page / activity"
Then I fill in the following: Then I fill in the following:
@ -34,6 +34,21 @@ Feature: Document tool
And wait for the page to be loaded And wait for the page to be loaded
Then I should see "My first document" Then I should see "My first document"
Scenario: Create a HTML document
Given I am on "/main/document/create_document.php?cidReq=TEMP"
Then I should see "Create a rich media page / activity"
Then I fill in the following:
| create_document_title | My second document |
And I fill in ckeditor field "content" with "<a href='www.chamilo.org'>Click here</a><span><strong>This is my second document!!!</strong></span>"
And I press "Create a rich media page / activity"
Then I should see "Item added"
And I should see "My second document"
Then I follow "My second document"
And wait for the page to be loaded
Then I should see "My second document"
And I should not see "<strong>"
And I should not see "www.chamilo.org"
Scenario: Upload a document Scenario: Upload a document
Given I am on "/main/document/upload.php?cidReq=TEMP" Given I am on "/main/document/upload.php?cidReq=TEMP"
Then I should see "Upload documents" Then I should see "Upload documents"

@ -232,6 +232,29 @@ Feature: Exercise tool
And I press "submitQuestion" And I press "submitQuestion"
Then I should see "Item added" Then I should see "Item added"
Scenario: Duplicate exercise
Given I am on "/main/exercise/exercise.php?cidReq=TEMP"
And I follow "Copy this exercise as a new one"
And I confirm the popup
Then I should see "Exercise copied"
And I should see "Exercise 1 - Copy"
Scenario: Import exercise to test questions categories
Given I am on "/main/exercise/upload_exercise.php?cidReq=TEMP"
And I should see "Import quiz from Excel"
And I attach the file "/tests/behat/uploadable_files/exercise.xls" to "user_upload_quiz"
When I press "Upload"
And wait for the page to be loaded
Then I should see "Exercise for Behat test"
Scenario: Import exercise from excel
Given I am on "/main/exercise/upload_exercise.php?cidReq=TEMP"
Then I should see "Import quiz from Excel"
Then I attach the file "/main/exercise/quiz_template.xls" to "user_upload_quiz"
And I press "Upload"
And wait for the page to be loaded
Then I should see "Definition of oligarchy"
Scenario: Try exercise "Exercise 1" Scenario: Try exercise "Exercise 1"
Given I am on "/main/exercise/exercise.php?cidReq=TEMP" Given I am on "/main/exercise/exercise.php?cidReq=TEMP"
And I follow "Exercise 1" And I follow "Exercise 1"
@ -303,28 +326,28 @@ Feature: Exercise tool
And I follow "Grade activity" And I follow "Grade activity"
Then I should see "Score for the test: 83 / 117" Then I should see "Score for the test: 83 / 117"
Scenario: Duplicate exercise Scenario: Create a session "Session Exercise" and add user "acostea"
Given I am on "/main/exercise/exercise.php?cidReq=TEMP" Given I am on "/main/session/session_add.php"
And I follow "Copy this exercise as a new one" When I fill in the following:
And I confirm the popup | name | Session Exercise |
Then I should see "Exercise copied" And I fill in select2 input "#coach_username" with id "1" and value "admin"
And I should see "Exercise 1 - Copy" And I press "submit"
Then wait for the page to be loaded
Scenario: Import exercise to test questions categories Then I should see "Add courses to this session (Session Exercise)"
Given I am on "/main/exercise/upload_exercise.php?cidReq=TEMP" Then I select "TEMP (TEMP)" from "NoSessionCoursesList[]"
And I should see "Import quiz from Excel" And I press "add_course"
And I attach the file "/tests/behat/uploadable_files/exercise.xls" to "user_upload_quiz" And I press "next"
When I press "Upload" Then I should see "Update successful"
And wait for the page to be loaded Then I follow "Multiple registration"
Then I should see "Exercise for Behat test" Then I select "Costea Andrea (acostea)" from "nosessionUsersList[]"
And I press "add_user"
And I press "next"
Then I should see "Update successful"
Scenario: Try exercise with categorized questions as student Scenario: Try exercise with categorized questions as student
Given I am a student subscribed to session "Session Exercise" Given I am a student
And I am on "/user_portal.php" And I am on course "TEMP" homepage in session "Session Exercise"
And I follow "Session Exercise" Then I should see "TEMP (Session Exercise)"
And wait for the page to be loaded
And I follow "tabs2"
And I follow "TEMP"
And I am on "/main/exercise/exercise.php?cidReq=TEMP" And I am on "/main/exercise/exercise.php?cidReq=TEMP"
And I follow "Exercise for Behat test" And I follow "Exercise for Behat test"
And I follow "Start test" And I follow "Start test"
@ -350,8 +373,10 @@ Feature: Exercise tool
And I select "B" from "choice_id_7_2" And I select "B" from "choice_id_7_2"
And I select "C" from "choice_id_7_3" And I select "C" from "choice_id_7_3"
And I press "Next question" And I press "Next question"
And wait for the page to be loaded
And I check "1" And I check "1"
And I press "Next question" And I press "Next question"
And wait for the page to be loaded
And I press "End test" And I press "End test"
Then I should see "Score for the test: 190 / 190" Then I should see "Score for the test: 190 / 190"
And I should see the table "#category_results": And I should see the table "#category_results":
@ -361,17 +386,15 @@ Feature: Exercise tool
| none | 80 / 60 | 133.33% | | none | 80 / 60 | 133.33% |
| Total | 190 / 190 | 100% | | Total | 190 / 190 | 100% |
Scenario: Teacher see exercise results by categories Scenario: Teacher looks at exercise results by categories
Given I am on "/user_portal.php" Given I am on "/user_portal.php"
And I follow "Session Exercise" And I am on course "TEMP" homepage in session "Session Exercise"
And wait for the page to be loaded Then I should see "TEMP (Session Exercise)"
And I follow "tabs2"
And I follow "TEMP"
And I am on "/main/exercise/exercise.php?cidReq=TEMP" And I am on "/main/exercise/exercise.php?cidReq=TEMP"
And I follow "Exercise for Behat test" And I follow "Exercise for Behat test"
And I follow "Results and feedback" And I follow "Results and feedback"
Then I should see "Learner score" Then I should see "Learner score"
And wait for the page to be loaded And wait very long for the page to be loaded
And I follow "Grade activity" And I follow "Grade activity"
Then I should see "Score for the test: 190 / 190" Then I should see "Score for the test: 190 / 190"
And I should see the table "#category_results": And I should see the table "#category_results":
@ -397,10 +420,9 @@ Feature: Exercise tool
And I follow "Delete" And I follow "Delete"
Then I should see "Category deleted" Then I should see "Category deleted"
Scenario: Import exercise from excel Scenario: Delete session
Given I am on "/main/exercise/upload_exercise.php?cidReq=TEMP" Given I am on "/main/session/session_list.php?keyword=Session+Exercise"
Then I should see "Import quiz from Excel"
Then I attach the file "/main/exercise/quiz_template.xls" to "user_upload_quiz"
And I press "Upload"
And wait for the page to be loaded And wait for the page to be loaded
Then I should see "Definition of oligarchy" And I follow "Delete"
And I confirm the popup
Then I should see "Deleted"

@ -6,6 +6,13 @@ Feature: Group tool
Given I am a platform administrator Given I am a platform administrator
And I am on course "TEMP" homepage And I am on course "TEMP" homepage
Scenario: Delete default category
Given I am on "/main/group/group.php?cidReq=TEMP&id_session=0"
Then I should see "Default groups"
Then I follow "Delete"
Then I confirm the popup
Then I should see "The category has been deleted"
Scenario: Create a group directory Scenario: Create a group directory
Given I am on "/main/group/group_category.php?cidReq=TEMP&id_session=0&action=add_category" Given I am on "/main/group/group_category.php?cidReq=TEMP&id_session=0&action=add_category"
When I fill in the following: When I fill in the following:
@ -13,17 +20,27 @@ Feature: Group tool
And I press "group_category_submit" And I press "group_category_submit"
Then I should see "Category created" Then I should see "Category created"
Scenario: Create a group Scenario: Create 4 groups
Given I am on "/main/group/group_creation.php?cidReq=TEMP&id_session=0" Given I am on "/main/group/group_creation.php?cidReq=TEMP&id_session=0"
When I fill in the following: When I fill in the following:
| number_of_groups | 1 | | number_of_groups | 5 |
And I press "submit" And I press "submit"
Then I should see "New groups creation" Then I should see "New groups creation"
When I fill in the following: When I fill in the following:
| group_0_places | 1 | | group_0_places | 1 |
| group_1_places | 1 |
| group_2_places | 1 |
| group_3_places | 1 |
| group_4_places | 2 |
And I fill in select bootstrap static by text "#category_0" select "Group category 1"
And I fill in select bootstrap static by text "#category_1" select "Group category 1"
And I fill in select bootstrap static by text "#category_2" select "Group category 1"
And I fill in select bootstrap static by text "#category_3" select "Group category 1"
And I fill in select bootstrap static by text "#category_4" select "Group category 1"
And I press "submit" And I press "submit"
Then I should see "group(s) has (have) been added" Then I should see "group(s) has (have) been added"
Scenario: Create document folder in group Scenario: Create document folder in group
Given I am on "/main/group/group.php?cidReq=TEMP&id_session=0" Given I am on "/main/group/group.php?cidReq=TEMP&id_session=0"
And I follow "Group 0001" And I follow "Group 0001"
@ -89,3 +106,300 @@ Feature: Group tool
Then wait for the page to be loaded Then wait for the page to be loaded
Then I should see "Are you sure to delete" Then I should see "Are you sure to delete"
Then I follow "delete_item" Then I follow "delete_item"
Scenario: Add fapple to the Group 0001
Given I am on "/main/group/group.php?cidReq=TEMP&id_session=0"
And I follow "Group 0001"
Then I should see "Group 0001"
Then I follow "Edit this group"
Then I should see "Group members"
Then wait for the page to be loaded
Then I follow "group_members_tab"
Then I select "Fiona Apple Maggart (fapple)" from "group_members"
Then I press "group_members_rightSelected"
Then I press "Save settings"
And wait for the page to be loaded
Then I should see "Group settings modified"
Then I follow "Group 0001"
Then I should see "Fiona"
Scenario: Add fapple to the Group 0003 not allowed because group category allows 1 user per group
Given I am on "/main/group/group.php?cidReq=TEMP&id_session=0"
And I follow "Group 0003"
Then I should see "Group 0003"
Then I follow "Edit this group"
Then I should see "Group members"
Then wait for the page to be loaded
Then I follow "group_members_tab"
Then I select "Fiona Apple Maggart (fapple)" from "group_members"
Then I press "group_members_rightSelected"
Then I press "Save settings"
And wait for the page to be loaded
Then I should see "Group settings modified"
Then I follow "Group 0003"
Then I should not see "Fiona"
# Group category overwrites all other groups settings.
Scenario: Change Group category to allow multiple inscription of the user
Given I am on "/main/group/group.php?cidReq=TEMP&id_session=0"
And I follow "Edit this category"
Then I should see "Edit group category: Group category 1"
Then I fill in select bootstrap static by text "#groups_per_user" select "10"
Then I press "Edit"
Then I should see "Group settings have been modified"
Scenario: Change Group 0003 settings to make announcements private
Given I am on "/main/group/group.php?cidReq=TEMP&id_session=0"
And I follow "Group 0003"
Then I should see "Group 0003"
Then I follow "Edit this group"
Then I check the "announcements_state" radio button with "2" value
Then I press "Save settings"
Then I should see "Group settings modified"
Scenario: Change Group 0004 settings to make it private
Given I am on "/main/group/group.php?cidReq=TEMP&id_session=0"
And I follow "Group 0004"
Then I should see "Group 0004"
Then I follow "Edit this group"
Then I check the "announcements_state" radio button with "2" value
Then I press "Save settings"
Then I should see "Group settings modified"
Scenario: Change Group 0005 settings to make announcements private between users
Given I am on "/main/group/group.php?cidReq=TEMP&id_session=0"
And I follow "Group 0005"
Then I should see "Group 0005"
Then I follow "Edit this group"
Then I check the "announcements_state" radio button with "3" value
Then I press "Save settings"
Then I should see "Group settings modified"
Scenario: Add fapple and acostea to Group 0005
Given I am on "/main/group/group.php?cidReq=TEMP&id_session=0"
And I follow "Group 0005"
Then I should see "Group 0005"
Then I follow "Edit this group"
Then I should see "Group members"
Then wait for the page to be loaded
Then I follow "group_members_tab"
Then I additionally select "Fiona Apple Maggart (fapple)" from "group_members"
Then I additionally select "Andrea Costea (acostea)" from "group_members"
Then I press "group_members_rightSelected"
Then I press "Save settings"
And wait for the page to be loaded
Then I should see "Group settings modified"
Then I follow "Group 0005"
Then I should see "Fiona"
Then I should see "Andrea"
Scenario: Add fapple to the Group 0003
Given I am on "/main/group/group.php?cidReq=TEMP&id_session=0"
And I follow "Group 0003"
Then I should see "Group 0003"
Then I follow "Edit this group"
Then I should see "Group members"
Then wait for the page to be loaded
Then I follow "group_members_tab"
Then I select "Fiona Apple Maggart (fapple)" from "group_members"
Then I press "group_members_rightSelected"
Then I press "Save settings"
And wait for the page to be loaded
Then I should see "Group settings modified"
Then I follow "Group 0003"
Then I should see "Fiona"
Scenario: Add acostea to the Group 0002
Given I am on "/main/group/group.php?cidReq=TEMP&id_session=0"
And I follow "Group 0002"
Then I should see "Group 0002"
Then I follow "Edit this group"
Then I should see "Group members"
Then wait for the page to be loaded
Then I follow "group_members_tab"
Then I select "Andrea Costea (acostea)" from "group_members"
Then I press "group_members_rightSelected"
Then I press "Save settings"
And wait for the page to be loaded
Then I should see "Group settings modified"
Then I follow "Group 0002"
Then I should see "Andrea"
Scenario: Create an announcement for everybody inside Group 0001
Given I am on "/main/group/group.php?cidReq=TEMP&id_session=0"
And I follow "Group 0001"
Then I should see "Group 0001"
And I follow "Announcements"
Then I should see "Announcements"
Then I follow "Add an announcement"
Then I should see "Add an announcement"
Then wait for the page to be loaded
Then I fill in the following:
| title | Announcement for all users inside Group 0001 |
And I fill in ckeditor field "content" with "Announcement description in Group 0001"
Then I follow "announcement_preview"
And wait for the page to be loaded
Then I should see "Announcement will be sent to"
Then I press "submit"
Then I should see "Announcement has been added"
Scenario: Create an announcement for fapple inside Group 0001
Given I am on "/main/group/group.php?cidReq=TEMP&id_session=0"
And I follow "Group 0001"
Then I should see "Group 0001"
And I follow "Announcements"
Then I should see "Announcements"
Then I follow "Add an announcement"
Then I should see "Add an announcement"
Then wait for the page to be loaded
Then I press "choose_recipients"
Then I select "Fiona Apple" from "users"
Then I press "users_rightSelected"
Then I fill in the following:
| title | Announcement for user fapple inside Group 0001 |
And I fill in ckeditor field "content" with "Announcement description for user fapple inside Group 0001"
Then I follow "announcement_preview"
And wait for the page to be loaded
Then I should see "Announcement will be sent to"
Then I press "submit"
Then I should see "Announcement has been added"
Scenario: Create an announcement for everybody inside Group 0003 (private)
Given I am on "/main/group/group.php?cidReq=TEMP&id_session=0"
And I follow "Group 0003"
Then I should see "Group 0003"
And I follow "Announcements"
Then I should see "Announcements"
Then I follow "Add an announcement"
Then I should see "Add an announcement"
Then wait for the page to be loaded
Then I fill in the following:
| title | Announcement for all users inside Group 0003 |
And I fill in ckeditor field "content" with "Announcement description in Group 0003"
Then I follow "announcement_preview"
And wait for the page to be loaded
Then I should see "Announcement will be sent to"
Then I press "submit"
Then I should see "Announcement has been added"
Scenario: Create an announcement for fapple inside Group 0003
Given I am on "/main/group/group.php?cidReq=TEMP&id_session=0"
And I follow "Group 0003"
Then I should see "Group 0003"
And I follow "Announcements"
Then I should see "Announcements"
Then I follow "Add an announcement"
Then I should see "Add an announcement"
Then wait for the page to be loaded
Then I press "choose_recipients"
Then I select "Fiona Apple" from "users"
Then I press "users_rightSelected"
Then I fill in the following:
| title | Announcement for user fapple inside Group 0003 |
And I fill in ckeditor field "content" with "Announcement description for user fapple inside Group 0003"
Then I follow "announcement_preview"
And wait for the page to be loaded
Then I should see "Announcement will be sent to"
Then I press "submit"
Then I should see "Announcement has been added"
Scenario: Create an announcement as acostea and send only to fapple
Given I am logged as "acostea"
Given I am on "/main/group/group.php?cidReq=TEMP&id_session=0"
And I follow "Group 0005"
Then I should see "Group 0005"
And I follow "Announcements"
Then I should see "Announcements"
Then I follow "Add an announcement"
Then I should see "Add an announcement"
Then wait for the page to be loaded
Then I press "choose_recipients"
Then I select "Fiona Apple Maggart" from "users"
Then I press "users_rightSelected"
Then I fill in the following:
| title | Announcement only for fapple Group 0005 |
And I fill in ckeditor field "content" with "Announcement description only for fapple Group 0005"
Then I follow "announcement_preview"
And wait for the page to be loaded
Then I should see "Announcement will be sent to"
Then I press "submit"
Then I should see "Announcement has been added"
Scenario: Check fapple/acostea access of announcements
Given I am logged as "fapple"
And I am on course "TEMP" homepage
And I am on "/main/group/group.php?cidReq=TEMP&id_session=0"
And I follow "Group 0001"
Then I should see "Group 0001"
Then I follow "Announcements"
And wait for the page to be loaded
Then I should see "Announcement for all users inside Group 0001"
Then I should see "Announcement for user fapple inside Group 0001"
Then I follow "Announcement for user fapple inside Group 0001 Group"
Then I should see "Announcement description for user fapple inside Group 0001"
Then I save current URL with name "announcement_for_user_fapple_group_0001_public"
Then I move backward one page
Then wait for the page to be loaded
Then I should see "Announcement for all users inside Group 0001"
Then I follow "Announcement for all users inside Group 0001"
Then I save current URL with name "announcement_for_all_users_group_0001_public"
Then I should see "Announcement description in Group 0001"
And I am on "/main/group/group.php?cidReq=TEMP&id_session=0"
And I follow "Group 0003"
Then I should see "Group 0003"
Then I follow "Announcements"
And wait for the page to be loaded
Then I should see "Announcement for all users inside Group 0003"
Then I should see "Announcement for user fapple inside Group 0003"
Then I follow "Announcement for user fapple inside Group 0003 Group"
Then I should see "Announcement description for user fapple inside Group 0003"
Then I save current URL with name "announcement_for_user_fapple_group_0003_private"
Then I move backward one page
Then wait for the page to be loaded
Then I should see "Announcement for all users inside Group 0003"
Then I follow "Announcement for all users inside Group 0003"
Then I should see "Announcement description in Group 0003"
Then I save current URL with name "announcement_for_all_users_group_0003_private"
And I am on "/main/group/group.php?cidReq=TEMP&id_session=0"
And I follow "Group 0005"
Then I should see "Group 0005"
Then I follow "Announcements"
And wait for the page to be loaded
Then I should see "Announcement only for fapple Group 0005"
Then I follow "Announcement only for fapple Group 0005"
Then I save current URL with name "announcement_only_for_fapple_private"
## Finish tests with fapple now check access with acostea ##
Given I am logged as "acostea"
And I am on course "TEMP" homepage
Given I am on "/main/group/group.php?cidReq=TEMP&id_session=0"
Then I should see "Group 0001"
And I should see "Group 0002"
And I should see "Group 0003"
And I should see "Group 0004"
Then I visit URL saved with name "announcement_for_user_fapple_group_0001_public"
Then I should see "Sorry, you are not allowed to access this page"
Then I visit URL saved with name "announcement_for_all_users_group_0001_public"
Then I should see "Sorry, you are not allowed to access this page"
Then I visit URL saved with name "announcement_only_for_fapple_private"
Then I should see "Sorry, you are not allowed to access this page"
Given I am logged as "acostea"
And I am on course "TEMP" homepage
Given I am on "/main/group/group.php?cidReq=TEMP&id_session=0"
Then I should see "Group 0001"
And I should see "Group 0002"
And I should see "Group 0003"
And I should see "Group 0004"
And I should see "Group 0005"
Then I visit URL saved with name "announcement_for_user_fapple_group_0001_public"
Then I should see "Sorry, you are not allowed to access this page"
Then I visit URL saved with name "announcement_for_all_users_group_0001_public"
Then I should see "Sorry, you are not allowed to access this page"
Then I visit URL saved with name "announcement_for_user_fapple_group_0003_private"
Then I should see "Sorry, you are not allowed to access this page"
Then I visit URL saved with name "announcement_for_all_users_group_0003_private"
Then I should see "Sorry, you are not allowed to access this page"
Then I visit URL saved with name "announcement_only_for_fapple_private"
Then I should see "Sorry, you are not allowed to access this page"

@ -48,11 +48,8 @@ Feature: LP tool
Then I should see "LP 1" Then I should see "LP 1"
And I should see "Document 1" And I should see "Document 1"
And I should see "Exercise 1" And I should see "Exercise 1"
And I am on "/main/lp/lp_controller.php?cidReq=TEMP&action=list"
Scenario: Delete a LP category Then I should see "LP 1"
Given I am on "/main/lp/lp_controller.php?cidReq=TEMP&action=list"
And I follow "Delete"
Then I should not see "LP category 1"
# Scenario: Check the PDF export in LP list if hide SCORM PDF link is false # Scenario: Check the PDF export in LP list if hide SCORM PDF link is false
# Given I am on "/main/admin/settings.php?category=Course" # Given I am on "/main/admin/settings.php?category=Course"
@ -68,8 +65,19 @@ Feature: LP tool
# And I am on "/main/lp/lp_controller.php?cidReq=TEMP&action=list&isStudentView=true" # And I am on "/main/lp/lp_controller.php?cidReq=TEMP&action=list&isStudentView=true"
# Then I should not see an icon with title "Export to PDF" # Then I should not see an icon with title "Export to PDF"
Scenario: LP exists and LP category exists
And I am on "/main/lp/lp_controller.php?cidReq=TEMP&id_session=0&gidReq=0&gradebook=0&origin="
Then I should see "LP 1"
And I should see "LP category 1"
Scenario: Delete a LP Scenario: Delete a LP
Given I am on "/main/lp/lp_controller.php?cidReq=TEMP&action=list" Given I am on "/main/lp/lp_controller.php?cidReq=TEMP"
And I follow "Delete" And I follow "Delete"
And I confirm the popup And I confirm the popup
Then I should not see "LP 1" Then I should not see "LP 1"
Scenario: Delete a LP category
Given I am on "/main/lp/lp_controller.php?cidReq=TEMP"
Then I should see "LP category 1"
And I follow "Delete"
Then I should see "Deleted"
Loading…
Cancel
Save