Merge remote-tracking branch 'chamilo/1.11.x' into 18214

* chamilo/1.11.x: (246 commits)
  Student publications: Teacher can upload corrections BT#18352
  Exercises: Fix overload page if allow record audio is activated.
  Exercises: Fix notification URL
  Update lang var + format code
  Student Publication: Pending list - fix access for teachers BT#18352
  Student Publication: Pending list - fix access for teachers BT#18352
  Admin: Add config lp_start_and_end_date_visible_in_student_view
  Exercises - Replace QuestionsToReview to VerificationOfAnsweredQuestions
  Minor - behat decrease wait time
  Minor - format code
  LPs: Hide category for student if hidden in course session BT#17744
  LPs: Fix category link visibility, dont add cidreq already added
  LPs: Allow invisible LP category in course home for students BT#17744
  LPs: Fix categoiry link visibility BT#17744
  Minor - format code, update doc
  Course category: Add $checkHidePrivate parameter BT#18434
  LPs: Fix course home LP link visibility BT#17744
  LPs: Fix course home LP link visibility BT#17744
  LPs: Fix course home LP link visibility BT#17744
  LPs: Fix course home LP link visibility BT#17744
  ...
pull/3743/head
Carlos Alvarado 5 years ago
commit 922aa797d0
No known key found for this signature in database
GPG Key ID: B612DB1EE6658FBB
  1. 191
      .github/workflows/php.yml
  2. 2
      .yamllint_config
  3. 3
      README.md
  4. 24
      app/Resources/public/css/base.css
  5. 4
      documentation/security.html
  6. 16
      main/admin/course_category.php
  7. 5
      main/admin/user_export.php
  8. 4
      main/admin/user_information.php
  9. 25
      main/announcements/announcements.php
  10. 1
      main/auth/courses.php
  11. 2
      main/auth/okn/start.php
  12. 8
      main/badge/issued.php
  13. 9
      main/badge/issued_all.php
  14. 3
      main/calendar/agenda.php
  15. 3
      main/calendar/agenda_list.php
  16. 1
      main/calendar/planification.php
  17. 27
      main/course_home/course_home.php
  18. 103
      main/course_info/delete_course.php
  19. 23
      main/course_info/infocours.php
  20. 1
      main/coursecopy/import_moodle.php
  21. 54
      main/coursecopy/recycle_course.php
  22. 3
      main/cron/update_ldap_users.php
  23. 2
      main/document/download_uploaded_files.php
  24. 82
      main/document/showinframes.php
  25. 92
      main/exercise/exercise.class.php
  26. 90
      main/exercise/exercise_question_reminder.php
  27. 5
      main/exercise/exercise_reminder.php
  28. 94
      main/exercise/exercise_report.php
  29. 6
      main/exercise/exercise_result.class.php
  30. 33
      main/exercise/exercise_result.php
  31. 10
      main/exercise/exercise_show.php
  32. 147
      main/exercise/exercise_submit.php
  33. 16
      main/exercise/export/aiken/aiken_import.inc.php
  34. 2
      main/exercise/result.php
  35. 44
      main/exercise/stats.php
  36. 13
      main/gradebook/certificate_report.php
  37. 31
      main/gradebook/cli/export_all_certificates.php
  38. 52
      main/gradebook/gradebook_display_certificate.php
  39. 47
      main/gradebook/gradebook_flatview.php
  40. 6
      main/gradebook/index.php
  41. 81
      main/gradebook/lib/GradebookUtils.php
  42. 29
      main/gradebook/lib/be/category.class.php
  43. 37
      main/gradebook/lib/fe/displaygradebook.php
  44. 2
      main/gradebook/lib/fe/evalform.class.php
  45. 23
      main/gradebook/lib/fe/flatviewtable.class.php
  46. 104
      main/gradebook/lib/flatview_data_generator.class.php
  47. 7
      main/gradebook/lib/gradebook_data_generator.class.php
  48. 35
      main/gradebook/lib/gradebook_result.class.php
  49. 74
      main/gradebook/lib/scoredisplay.class.php
  50. 5
      main/group/group.php
  51. BIN
      main/img/icons/32/file_html_na.png
  52. 3
      main/inc/ajax/announcement.ajax.php
  53. 12
      main/inc/ajax/course.ajax.php
  54. 17
      main/inc/ajax/course_home.ajax.php
  55. 53
      main/inc/ajax/exercise.ajax.php
  56. 4
      main/inc/ajax/extra_field.ajax.php
  57. 38
      main/inc/ajax/gradebook.ajax.php
  58. 56
      main/inc/ajax/model.ajax.php
  59. 9
      main/inc/ajax/work.ajax.php
  60. 25
      main/inc/lib/AnnouncementManager.php
  61. 10
      main/inc/lib/CoursesAndSessionsCatalog.class.php
  62. 45
      main/inc/lib/MoodleImport.php
  63. 1542
      main/inc/lib/PortfolioController.php
  64. 2
      main/inc/lib/SortableTableFromArrayConfig.php
  65. 4
      main/inc/lib/agenda.lib.php
  66. 3
      main/inc/lib/api.lib.php
  67. 10
      main/inc/lib/auth.lib.php
  68. 11
      main/inc/lib/career.lib.php
  69. 88
      main/inc/lib/course.lib.php
  70. 23
      main/inc/lib/course_category.lib.php
  71. 200
      main/inc/lib/course_home.lib.php
  72. 33
      main/inc/lib/display.lib.php
  73. 37
      main/inc/lib/document.lib.php
  74. 181
      main/inc/lib/exercise.lib.php
  75. 3
      main/inc/lib/extra_field.lib.php
  76. 2
      main/inc/lib/groupmanager.lib.php
  77. 30
      main/inc/lib/hook/HookPortfolioItemAdded.php
  78. 27
      main/inc/lib/hook/HookPortfolioItemCommented.php
  79. 14
      main/inc/lib/hook/interfaces/HookPortfolioItemAddedEventInterface.php
  80. 16
      main/inc/lib/hook/interfaces/HookPortfolioItemAddedObserverInterface.php
  81. 14
      main/inc/lib/hook/interfaces/HookPortfolioItemCommentedEventInterface.php
  82. 16
      main/inc/lib/hook/interfaces/HookPortfolioItemCommentedObserverInterface.php
  83. 4
      main/inc/lib/hook/interfaces/HookQuizEndObserverInterface.php
  84. 14
      main/inc/lib/javascript/ckeditor/plugins/oembed/lang/fr.js
  85. 9
      main/inc/lib/javascript/record_audio/record_audio.js
  86. 17
      main/inc/lib/pdf.lib.php
  87. 2
      main/inc/lib/security.lib.php
  88. 4
      main/inc/lib/template.lib.php
  89. 17
      main/inc/lib/usermanager.lib.php
  90. 26
      main/install/configuration.dist.php
  91. 47
      main/lang/english/trad4all.inc.php
  92. 49
      main/lang/french/trad4all.inc.php
  93. 47
      main/lang/spanish/trad4all.inc.php
  94. 151
      main/lp/learnpath.class.php
  95. 31
      main/lp/learnpathList.class.php
  96. 1
      main/lp/lp_add.php
  97. 27
      main/lp/lp_controller.php
  98. 9
      main/lp/lp_list.php
  99. 22
      main/lp/lp_view.php
  100. 66
      main/mySpace/myStudents.php
  101. Some files were not shown because too many files have changed in this diff Show More

@ -0,0 +1,191 @@
name: PHP Composer
on: [push, pull_request]
jobs:
build:
name: PHP ${{ matrix.php-versions }} Test on ${{ matrix.operating-system }}
runs-on: ubuntu-18.04
strategy:
fail-fast: false
matrix:
operating-system: [ubuntu-latest]
php-versions: ['7.1', '7.2', '7.3', '7.4']
# services:
# mysql:
# image: mysql:5.7
# env:
# MYSQL_ALLOW_EMPTY_PASSWORD: false
# MYSQL_ROOT_PASSWORD: chamilo
# MYSQL_DATABASE: chamilo
# ports:
# - 3306/tcp
# options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Setup apache
run: |
sudo apt update
sudo apt install apache2 php${{ matrix.php-versions }} php${{ matrix.php-versions }}-common php${{ matrix.php-versions }}-cli libapache2-mod-php${{ matrix.php-versions }}
sudo a2enmod rewrite actions
sudo cp -f tests/travis/gh-apache /etc/apache2/sites-available/000-default.conf
sudo chmod 777 -R $HOME
cat /etc/apache2/sites-available/000-default.conf
sudo service apache2 restart
sudo systemctl restart apache2
- name: Setup PHP, with composer and extensions
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-versions }}
extensions: mbstring, xml, ctype, iconv, intl, pdo, pdo_mysql, dom, gd, json, soap, zip, bcmath
ini-values: post_max_size=256M, max_execution_time=600, memory_limit=4096M
- name: Get composer cache directory
id: composer-cache-v1
run: echo "::set-output name=dir::$(composer config cache-files-dir)"
- name: Cache composer dependencies
uses: actions/cache@v2
with:
path: ${{ steps.composer-cache-v1.outputs.dir }}
# Use composer.json for key, if composer.lock is not committed.
# key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }}
key: v1-${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
restore-keys: v1-${{ runner.os }}-composer-
- name: Install dependencies with composer
run: composer install --prefer-dist --no-progress
# - name: Install assets
# run: php bin/console assets:install
#
# - name: Generate fos_js_routes.json
# run: php bin/console fos:js-routing:dump --format=json --target=public/js/fos_js_routes.json
# - name: Get yarn cache directory
# id: yarn-cache
# run: echo "::set-output name=dir::$(yarn cache dir)"
#
# - name: Cache yarn dependencies
# uses: actions/cache@v2
# with:
# path: ${{ steps.yarn-cache.outputs.dir }}
# # Use composer.json for key, if composer.lock is not committed.
# # key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }}
# key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
# restore-keys: ${{ runner.os }}-yarn-
#
# - name: Yarn install
# uses: borales/actions-yarn@v2.3.0
# with:
# cmd: install # will run `yarn install` command
#
# - name: Yarn run encore dev
# uses: borales/actions-yarn@v2.3.0
# with:
# cmd: run encore dev
- name: Start chrome
run: |
sudo apt install google-chrome-stable
google-chrome --version
google-chrome-stable --version
google-chrome-stable --headless --disable-gpu --remote-debugging-port=9222 http://localhost &
whereis google-chrome-stable
# https://github.com/marketplace/actions/setup-chromedriver
- name: Start chromedriver
uses: nanasess/setup-chromedriver@master
with:
# Optional: do not specify to match Chrome's version
chromedriver-version: '88.0.4324.96'
- run: |
export DISPLAY=:99
chromedriver --url-base=/wd/hub &
sudo Xvfb -ac :99 -screen 0 1280x1024x24 > /dev/null 2>&1 & # optional
# wget https://chromedriver.storage.googleapis.com/88.0.4324.96/chromedriver_linux64.zip --quiet && unzip chromedriver_linux64.zip
# sudo mv chromedriver /usr/bin
# sudo chmod +x /usr/bin/chromedriver
# chromedriver --version
# - run: |
# export DISPLAY=:99
# chromedriver --url-base=/wd/hub &
# sudo Xvfb -ac :99 -screen 0 1280x1024x24 > /dev/null 2>&1 & # optional
# chromedriver --version
- name: Set up JDK 1.8
uses: actions/setup-java@v1
with:
java-version: 1.8
- name: Get Selenium
run: |
wget http://selenium-release.storage.googleapis.com/3.141/selenium-server-standalone-3.141.59.jar --quiet
- name: Run Selenium
run: |
java -version
export DISPLAY=:99.0
sudo xvfb -ac :99 -screen 0 1280x1024x24 > /dev/null 2>&1 &
java -Dwebdriver.chrome.driver=/usr/bin/chromedriver -jar selenium-server-standalone-3.141.59.jar -log selenium.log > /dev/null &
- name: Start mysql service
run: |
sudo /etc/init.d/mysql start
sudo systemctl restart apache2
#
# - name: Install database
# run: |
# bin/console doctrine:database:create || echo "Error while creating the DB"
# env:
# DATABASE_URL: mysql://root:root@127.0.0.1:${{ job.services.mysql.ports['3306'] }}/chamilo
# DATABASE_HOST: 127.0.0.1
# DATABASE_PORT: ${{ job.services.mysql.ports['3306'] }}
# DATABASE_NAME: chamilo
# DATABASE_USER: root
# DATABASE_PASSWORD: root
# APP_INSTALLED: 1
- name: Check settings
run: |
php -ini
php -v
php -m
# - name: Check chamilo
# run: |
# sudo chmod 777 -R ${{ github.workspace }}
# curl http://localhost/main/install/index.php
- name: Install chash
run: |
git clone https://github.com/chamilo/chash
cd chash
git checkout 0.2.x
composer install
php -d phar.readonly=0 createPhar.php
chmod +x chash.phar
sudo mv chash.phar /usr/local/bin/chash
- name: Install chamilo
run: |
php ${{ github.workspace }}/chash/chash.php chash:chamilo_install 1.11.x ${{ github.workspace }} --no-interaction --sitename="Chamilo" --site_url="http://localhost/" --institution="Chamilo" --institution_url="https://chamilo.org" --encrypt_method="sha1" --firstname="John" --lastname="Doe" --language="english" --driver="pdo_mysql" --host="localhost" --port="3306" --dbname="chamilo" --dbuser="root" --dbpassword="root" --permissions_for_new_directories="0777" --permissions_for_new_files="0666" --linux-user="www-data" --linux-group="www-data" --username="admin" --password="admin" --email="admin@example.com" --phone="555-5555"
sudo chmod -R 777 app/cache app/logs app/courses app/upload web
php ${{ github.workspace }}/chash/chash.php chash:chamilo_status
ls -la
bash tests/travis/post_installation.sh
curl http://localhost/index.php
ls -la web/build
- name: Behat tests
run: |
cd ${{ github.workspace }}/tests/behat
../../vendor/behat/behat/bin/behat -vvv

@ -8,4 +8,4 @@ ignore: |
rules:
line-length:
max: 500
max: 800

@ -1,8 +1,7 @@
# Chamilo 1.11.x
[![Build Status](https://travis-ci.org/chamilo/chamilo-lms.svg?branch=1.11.x)](https://travis-ci.org/chamilo/chamilo-lms)
![PHP Composer](https://github.com/chamilo/chamilo-lms/workflows/PHP%20Composer/badge.svg?branch=1.11.x)
[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/chamilo/chamilo-lms/badges/quality-score.png?b=1.11.x)](https://scrutinizer-ci.com/g/chamilo/chamilo-lms/?branch=1.11.x)
[![Code Coverage](https://scrutinizer-ci.com/g/chamilo/chamilo-lms/badges/coverage.png?b=1.11.x)](https://scrutinizer-ci.com/g/chamilo/chamilo-lms/?branch=1.11.x)
[![Bountysource](https://www.bountysource.com/badge/team?team_id=12439&style=raised)](https://www.bountysource.com/teams/chamilo?utm_source=chamilo&utm_medium=shield&utm_campaign=raised)
[![Code Consistency](https://squizlabs.github.io/PHP_CodeSniffer/analysis/chamilo/chamilo-lms/grade.svg)](http://squizlabs.github.io/PHP_CodeSniffer/analysis/chamilo/chamilo-lms/)
[![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/166/badge)](https://bestpractices.coreinfrastructure.org/projects/166)

@ -6111,6 +6111,10 @@ div#chat-remote-video video {
border-color: #0077b5;
}
.txt-linkedin {
color: #0a66c2;
}
/**** END SOCIAL MEDIA ****/
/**** ABOUT SESSION ****/
@ -6576,23 +6580,6 @@ div#chat-remote-video video {
text-align: center;
}
.grid-courses .items .image .black-shadow {
top: 0;
left: 0;
width: 100%;
height: 100%;
opacity: 0;
box-sizing: border-box;
position: absolute;
background: rgba(0, 0, 0, 0.7) none repeat scroll 0 0;
padding: 40px 10px 10px;
}
.grid-courses .items .image .hovered-course {
opacity: 1;
transition: all 0.3s ease 0s;
}
.grid-courses .items .toolbar .info {
float: left;
font-size: 12px;
@ -10542,7 +10529,7 @@ ul.dropdown-menu.inner > li > a {
#registration input.register-profile[value='5']:after,
#registration input.register-profile[value='1']:after
{
top:-5px;
top: -5px;
left: -2px;
}
@ -10555,4 +10542,3 @@ ul.dropdown-menu.inner > li > a {
.form-inline .bootstrap-select.form-control:not([class*="col-"]) {
width: 100%;
}

@ -43,9 +43,7 @@ It is considered a safer behaviour not to disclose server information from your
<p>
Make sure you check <a href="http://support.chamilo.org/projects/chamilo-18/wiki/Security_issues">our security
issues page</a> from time to time.
Subscribe to our free security alerts mailing-list:
<a href="http://lists.chamilo.org/listinfo/security">http://lists.chamilo.org/listinfo/security</a> or that you
follow our security Twitter feed: <a href="http://twitter.com/chamilosecurity">http://twitter.com/chamilosecurity</a>.
You can also follow our security Twitter feed: <a href="http://twitter.com/chamilosecurity">http://twitter.com/chamilosecurity</a>.
</p>
<h2><a name="3.Using-safe-browsers"></a>3. Using safe browsers</h2>

@ -23,13 +23,13 @@ $action = isset($_GET['action']) ? $_GET['action'] : null;
$myCourseListAsCategory = api_get_configuration_value('my_courses_list_as_category');
if (!empty($action)) {
if ($action == 'delete') {
if ($action === 'delete') {
CourseCategory::deleteNode($categoryId);
Display::addFlash(Display::return_message(get_lang('Deleted')));
header('Location: '.api_get_self().'?category='.Security::remove_XSS($category));
exit();
} elseif (($action == 'add' || $action == 'edit') && isset($_POST['formSent']) && $_POST['formSent']) {
if ($action == 'add') {
} elseif (($action === 'add' || $action === 'edit') && isset($_POST['formSent']) && $_POST['formSent']) {
if ($action === 'add') {
$ret = CourseCategory::addNode(
$_POST['code'],
$_POST['name'],
@ -63,7 +63,7 @@ if (!empty($action)) {
Display::addFlash($errorMsg);
header('Location: '.api_get_path(WEB_CODE_PATH).'admin/course_category.php');
exit;
} elseif ($action == 'moveUp') {
} elseif ($action === 'moveUp') {
CourseCategory::moveNodeUp($categoryId, $_GET['tree_pos'], $category);
header('Location: '.api_get_self().'?category='.Security::remove_XSS($category));
Display::addFlash(Display::return_message(get_lang('Updated')));
@ -80,7 +80,7 @@ $interbreadcrumb[] = [
Display::display_header($tool_name);
$urlId = api_get_current_access_url_id();
if ($action == 'add' || $action == 'edit') {
if ($action === 'add' || $action === 'edit') {
echo '<div class="actions">';
echo Display::url(
Display::return_icon('folder_up.png', get_lang('Back'), '', ICON_SIZE_MEDIUM),
@ -88,7 +88,7 @@ if ($action == 'add' || $action == 'edit') {
);
echo '</div>';
$form_title = ($action == 'add') ? get_lang('AddACategory') : get_lang('EditNode');
$form_title = $action === 'add' ? get_lang('AddACategory') : get_lang('EditNode');
if (!empty($category)) {
$form_title .= ' '.get_lang('Into').' '.Security::remove_XSS($category);
}
@ -139,7 +139,7 @@ if ($action == 'add' || $action == 'edit') {
['ToolbarSet' => 'Minimal']
);
$form->addFile('image', get_lang('Image'), ['accept' => 'image/*']);
if ($action == 'edit' && !empty($categoryInfo['image'])) {
if ($action === 'edit' && !empty($categoryInfo['image'])) {
$form->addHtml('
<div class="form-group">
<div class="col-sm-offset-2 col-sm-8">'.
@ -180,7 +180,7 @@ if ($action == 'add' || $action == 'edit') {
);
}
if (empty($parentInfo) || $parentInfo['auth_cat_child'] == 'TRUE') {
if (empty($parentInfo) || $parentInfo['auth_cat_child'] === 'TRUE') {
$newCategoryLink = Display::url(
Display::return_icon('new_folder.png', get_lang('AddACategory'), '', ICON_SIZE_MEDIUM),
api_get_path(WEB_CODE_PATH).'admin/course_category.php?action=add&category='.Security::remove_XSS($category)

@ -88,7 +88,8 @@ if ($form->validate()) {
u.status AS Status,
u.official_code AS OfficialCode,
u.phone AS Phone,
u.registration_date AS RegistrationDate";
u.registration_date AS RegistrationDate,
u.active AS Active";
if (strlen($course_code) > 0) {
$sql .= " FROM $user_table u, $course_user_table cu
WHERE
@ -138,6 +139,7 @@ if ($form->validate()) {
'OfficialCode',
'PhoneNumber',
'RegistrationDate',
'Active',
];
} else {
$data[] = [
@ -152,6 +154,7 @@ if ($form->validate()) {
'OfficialCode',
'PhoneNumber',
'RegistrationDate',
'Active',
];
}

@ -718,8 +718,8 @@ if (api_get_configuration_value('allow_career_users')) {
$table = new HTML_Table(['class' => 'table table-hover table-striped data_table']);
$table->setHeaderContents(0, 0, get_lang('Career'));
$row = 1;
foreach ($careers as $carerData) {
$table->setCellContents($row, 0, $carerData['name']);
foreach ($careers as $careerData) {
$table->setCellContents($row, 0, $careerData['name']);
$row++;
}
echo $table->toHtml();

@ -23,17 +23,18 @@ $token = Security::get_existing_token();
$courseId = api_get_course_int_id();
$_course = api_get_course_info_by_id($courseId);
$group_id = api_get_group_id();
$sessionId = api_get_session_id();
$current_course_tool = TOOL_ANNOUNCEMENT;
$this_section = SECTION_COURSES;
$nameTools = get_lang('ToolAnnouncement');
$allowToEdit = (
api_is_allowed_to_edit(false, true) ||
(api_get_course_setting('allow_user_edit_announcement') && !api_is_anonymous())
(api_get_course_setting('allow_user_edit_announcement') && !api_is_anonymous()) ||
($sessionId && api_is_coach() && api_get_configuration_value('allow_coach_to_edit_announcements'))
);
$allowStudentInGroupToSend = false;
$sessionId = api_get_session_id();
$drhHasAccessToSessionContent = api_drh_can_access_all_session_content();
if (!empty($sessionId) && $drhHasAccessToSessionContent) {
$allowToEdit = $allowToEdit || api_is_drh();
@ -344,13 +345,19 @@ switch ($action) {
exit;
break;
case 'showhide':
if (!isset($_GET['isStudentView']) || $_GET['isStudentView'] != 'false') {
if (!isset($_GET['isStudentView']) || $_GET['isStudentView'] !== 'false') {
if (isset($_GET['id']) && $_GET['id']) {
if ($sessionId != 0 &&
api_is_allowed_to_session_edit(false, true) == false
) {
$block = true;
if (api_get_configuration_value('allow_coach_to_edit_announcements') && api_is_coach()) {
$block = false;
}
if ($block) {
api_not_allowed();
}
}
if (!$allowToEdit) {
api_not_allowed(true);
@ -372,10 +379,14 @@ switch ($action) {
break;
case 'add':
case 'modify':
if ($sessionId != 0 &&
api_is_allowed_to_session_edit(false, true) == false
) {
api_not_allowed(true);
if ($sessionId != 0 && api_is_allowed_to_session_edit(false, true) === false) {
$block = true;
if (api_get_configuration_value('allow_coach_to_edit_announcements') && api_is_coach()) {
$block = false;
}
if ($block) {
api_not_allowed();
}
}
if ($allowStudentInGroupToSend === false) {

@ -284,6 +284,7 @@ switch ($action) {
$categoryCode,
$searchTerm,
true,
true,
$conditions
);
}

@ -25,7 +25,7 @@ $auth = new Auth($settingsInfo);
$settings = new Settings($settingsInfo);
$authRequest = new AuthnRequest($settings);
$samlRequest = $authRequest->getRequest();
$samlRequest = $authRequest->getRequest(true);
$idpData = $settings->getIdPData();
if (isset($_GET['email']) || isset($_GET['email_bis'])) {

@ -88,10 +88,18 @@ if ($skillIssue->getAcquiredLevel()) {
}
$author = api_get_user_info($skillIssue->getArgumentationAuthorId());
$tempDate = DateTime::createFromFormat('Y-m-d H:i:s', $skillIssueDate);
$linkedinOrganizationId = api_get_configuration_value('linkedin_organization_id');
if (($linkedinOrganizationId === false)) {
$linkedinOrganizationId = null;
}
$skillIssueInfo = [
'id' => $skillIssue->getId(),
'datetime' => api_format_date($skillIssueDate, DATE_TIME_FORMAT_SHORT),
'year' => $tempDate->format('Y'),
'month' => $tempDate->format('m'),
'linkedin_organization_id' => $linkedinOrganizationId,
'acquired_level' => $currentSkillLevel,
'argumentation_author_id' => $skillIssue->getArgumentationAuthorId(),
'argumentation_author_name' => $author['complete_name'],

@ -72,9 +72,18 @@ foreach ($userSkills as $index => $skillIssue) {
}
$argumentationAuthor = api_get_user_info($skillIssue->getArgumentationAuthorId());
$tempDate = DateTime::createFromFormat('Y-m-d H:i:s', $skillIssueDate);
$linkedinOrganizationId = api_get_configuration_value('linkedin_organization_id');
if (($linkedinOrganizationId === false)) {
$linkedinOrganizationId = null;
}
$skillIssueInfo = [
'id' => $skillIssue->getId(),
'datetime' => api_format_date($skillIssueDate, DATE_TIME_FORMAT_SHORT),
'year' => $tempDate->format('Y'),
'month' => $tempDate->format('m'),
'linkedin_organization_id' => $linkedinOrganizationId,
'acquired_level' => $currentSkillLevel,
'argumentation_author_id' => $skillIssue->getArgumentationAuthorId(),
'argumentation_author_name' => api_get_person_name(

@ -1,4 +1,5 @@
<?php
/* For licensing terms, see /license.txt */
// use anonymous mode when accessing this course tool
@ -16,7 +17,7 @@ $action = isset($_GET['action']) ? Security::remove_XSS($_GET['action']) : null;
$url = null;
if (empty($action)) {
if (!empty($course_info)) {
$url = api_get_path(WEB_CODE_PATH).'calendar/agenda_js.php?type=course'.'&'.api_get_cidreq();
$url = api_get_path(WEB_CODE_PATH).'calendar/agenda_js.php?type=course&'.api_get_cidreq();
} else {
$url = api_get_path(WEB_CODE_PATH).'calendar/agenda_js.php?';
}

@ -12,7 +12,8 @@ $logInfo = [
];
Event::registerLog($logInfo);
$type = isset($_REQUEST['type']) ? $_REQUEST['type'] : null;
$typeList = ['personal', 'course', 'admin', 'platform'];
$type = isset($_REQUEST['type']) && in_array($_REQUEST['type'], $typeList, true) ? $_REQUEST['type'] : null;
$interbreadcrumb[] = [
'url' => api_get_path(WEB_CODE_PATH).'calendar/agenda_js.php?type='.Security::remove_XSS($type),

@ -1,4 +1,5 @@
<?php
/* For licensing terms, see /license.txt */
use Chamilo\CoreBundle\Component\Utils\ChamiloApi;

@ -34,8 +34,7 @@ require_once __DIR__.'/../inc/global.inc.php';
$js = '<script>'.api_get_language_translate_html().'</script>';
$htmlHeadXtra[] = $js;
$htmlHeadXtra[] = '<script type="text/javascript">
$htmlHeadXtra[] = '<script>
/* show eye for all show/hide*/
function buttonForAllShowHide()
{
@ -53,7 +52,6 @@ $htmlHeadXtra[] = '<script type="text/javascript">
// if the image has the eye-slash icon, prepare to make visible
tools_visibles.push(image_id)
}
});
if (tools_visibles.length == 0) {
$(".visible-all").addClass("hidden");
@ -62,7 +60,8 @@ $htmlHeadXtra[] = '<script type="text/javascript">
$(".visible-all").removeClass("hidden");
$(".invisible-all").addClass("hidden");
}
};
}
/* option show/hide thematic-block */
$(function() {
buttonForAllShowHide();
@ -81,11 +80,11 @@ $htmlHeadXtra[] = '<script type="text/javascript">
tools_visibles.push(image_id)
}
});
message_invisible = "'.get_lang('ToolIsNowHidden', '').'";
message_invisible = "'.get_lang('ToolIsNowHidden').'";
ids = tools_invisibles;
if (tools_invisibles.length == 0) {
ids = tools_visibles;
message_invisible = "'.get_lang('ToolIsNowVisible', '').'";
message_invisible = "'.get_lang('ToolIsNowVisible').'";
}
$.ajax({
@ -122,6 +121,7 @@ $htmlHeadXtra[] = '<script type="text/javascript">
}
});
});
$("#thematic-show").click(function(){
$(".btn-hide-thematic").hide();
$(".btn-show-thematic").show(); //show using class
@ -162,6 +162,8 @@ $htmlHeadXtra[] = '<script type="text/javascript">
//eyes
//$("#" + tool_id).attr("src", new_current_view);
$("#linktool_"+my_tool_id).attr("class", info.fclass);
$("#linktool_"+my_tool_id).attr("title", info.label);
//tool
$("#toolimage_" + my_tool_id).attr("src", new_current_tool_image);
//clase
@ -169,15 +171,15 @@ $htmlHeadXtra[] = '<script type="text/javascript">
$("#istooldesc_" + my_tool_id).attr("class", info.tclass);
if (info.message == "is_active") {
message = "'.get_lang('ToolIsNowVisible', '').'";
message = "'.get_lang('ToolIsNowVisible').'";
$("#" + tool_id)
.attr("alt", "'.get_lang('Activate', '').'")
.attr("title", "'.get_lang('Activate', '').'");
.attr("alt", "'.get_lang('Deactivate').'")
.attr("title", "'.get_lang('Deactivate').'");
} else {
message = "'.get_lang('ToolIsNowHidden', '').'";
message = "'.get_lang('ToolIsNowHidden').'";
$("#" + tool_id)
.attr("alt", "'.get_lang('Deactivate', '').'")
.attr("title", "'.get_lang('Deactivate', '').'");
.attr("alt", "'.get_lang('Activate').'")
.attr("title", "'.get_lang('Activate').'");
}
$(".normal-message").hide();
$("#id_confirmation_message").html(message);
@ -186,7 +188,6 @@ $htmlHeadXtra[] = '<script type="text/javascript">
});
});
});
</script>';
// The section for the tabs

@ -15,71 +15,76 @@ $current_course_tool = TOOL_COURSE_MAINTENANCE;
api_protect_course_script(true);
$_course = api_get_course_info();
$current_course_code = $_course['official_code'];
$current_course_name = $_course['name'];
$courseInfo = api_get_course_info();
if (empty($courseInfo)) {
api_not_allowed(true);
}
$courseCode = $courseInfo['code'];
$courseName = $courseInfo['name'];
if (!api_is_allowed_to_edit()) {
api_not_allowed(true);
}
$tool_name = get_lang('DelCourse');
$type_info_message = 'warning';
if (isset($_GET['delete']) && $_GET['delete'] === 'yes' && $_GET['course_code'] && !empty($_GET['course_code'])) {
if ($current_course_code == $_GET['course_code']) {
CourseManager::delete_course($_course['sysCode']);
$content = Display::page_subheader(get_lang('CourseTitle').' : '.$courseName);
$message = '';
$message .= Display::return_message(
'<strong>'.get_lang('ByDel').'<strong>',
'error',
false
);
$form = new FormValidator('delete', 'get');
$form->addLabel(null, sprintf(get_lang('CourseCodeToEnteredCapitalLettersToConfirmDeletionX'), $courseCode));
$form->addText('course_code', get_lang('CourseCode'));
$form->addLabel(null, get_lang('AreYouSureToDeleteJS'));
$buttonGroup[] = $form->addButton('yes', get_lang('Yes'), '', 'danger', '', '', [], true);
$buttonGroup[] = $form->addButton('no', get_lang('No'), '', 'primary', '', '', ['id' => 'no_delete'], true);
$form->addGroup($buttonGroup);
$returnUrl = api_get_path(WEB_CODE_PATH).'course_info/maintenance.php?'.api_get_cidreq();
if ($form->validate()) {
$values = $form->getSubmitValues();
$courseCodeFromGet = $values['course_code'];
if (isset($values['no'])) {
api_location($returnUrl);
}
if (isset($values['yes'])) {
if ($courseCode === $courseCodeFromGet) {
CourseManager::delete_course($courseInfo['code']);
// DELETE CONFIRMATION MESSAGE
Session::erase('_cid');
Session::erase('_real_cid');
$message = '<h3>'.get_lang('CourseTitle').' : '.$current_course_name.'</h3>';
$message .= '<h3>'.get_lang('CourseCode').' : '.$current_course_code.'</h3>';
$message .= get_lang('HasDel');
$message .= '<br /><br /><a href="../../index.php">'.get_lang('BackHome').'</a>';
Display::addFlash(Display::return_message($courseCode.' '.get_lang('HasDel'), 'error', false));
api_location(api_get_path(WEB_PATH));
} else {
/* message if code course is incorrect */
$message = '<h3>'.get_lang('CourseTitle').' : '.$current_course_name.'</h3>';
$message .= '<h3>'.get_lang('CourseCode').' : '.$current_course_code.'</h3>';
$message .= '<p>'.get_lang('CourseRegistrationCodeIncorrect').'</p>';
$message .= '<p><a class="btn btn-primary" href="'
.api_get_path(WEB_CODE_PATH)
.'course_info/delete_course.php?'
.api_get_cidreq()
.'">'.get_lang('BackToPreviousPage').'</a>';
$message .= '<br /><br /><a href="../../index.php">'.get_lang('BackHome').'</a>';
$type_info_message = 'error';
Display::addFlash(Display::return_message(get_lang('CourseRegistrationCodeIncorrect'), 'warning'));
}
}
} else {
$message = '<h3>'.get_lang('CourseTitle').' : '.$current_course_name.'</h3>';
$message .= '<h3>'.get_lang('CourseCode').' : '.$current_course_code.'</h3>';
$message .= '<p>'.get_lang('ByDel').'</p>';
$message .= '<p><span class="form_required">*</span>'
.get_lang('CourseCodeConfirmation')
.'&nbsp;<input type="text" name="course_code" id="course_code"></p>';
$message .= '<p>';
$message .= '<button class="btn btn-danger delete-course">'.get_lang('ValidateChanges').'</button>';
$message .= '&nbsp;';
$message .= '<a class="btn btn-primary"href="'
.api_get_path(WEB_CODE_PATH)
.'course_info/maintenance.php?'
.api_get_cidreq().'">'
.get_lang('No')
.'</a>';
$message .= '</p>';
$interbreadcrumb[] = [
}
$message .= $form->returnForm();
$interbreadcrumb[] = [
'url' => 'maintenance.php',
'name' => get_lang('Maintenance'),
];
}
$htmlHeadXtra[] = '<script>
];
$htmlHeadXtra[] = '<script>
$(function(){
/* Asking by course code to confirm recycling*/
$(".delete-course").on("click",function(){
window.location ="'.api_get_self().'?delete=yes&'.api_get_cidreq().'&course_code=" + $("#course_code").val();
})
$("#no_delete").on("click", function(e) {
e.preventDefault();
window.location = "'.$returnUrl.'";
});
})
</script>';
$tpl = new Template($tool_name);
$tpl->assign('content', Display::return_message($message, $type_info_message, false));
$tpl->assign('content', $content.$message);
$tpl->display_one_col_template();

@ -922,6 +922,29 @@ $form->addPanelOption(
$globalGroup
);
if (api_get_configuration_value('allow_portfolio_tool')) {
$globalGroup = [
get_lang('QualifyPortfolioItems') => [
$form->createElement('radio', 'qualify_portfolio_item', null, get_lang('Yes'), 1),
$form->createElement('radio', 'qualify_portfolio_item', null, get_lang('No'), 2),
],
get_lang('QualifyPortfolioComments') => [
$form->createElement('radio', 'qualify_portfolio_comment', null, get_lang('Yes'), 1),
$form->createElement('radio', 'qualify_portfolio_comment', null, get_lang('No'), 2),
],
get_lang('MaxScore') => [
$form->createElement('number', 'portfolio_max_score', get_lang('MaxScore'), ['step' => 'any', 'min' => 0]),
],
$form->addButtonSave(get_lang('SaveSettings'), 'submit_save', true),
];
$form->addPanelOption(
'portfolio',
Display::return_icon('wiki_task.png', get_lang('Portfolio')).PHP_EOL.get_lang('Portfolio'),
$globalGroup
);
}
// Plugin course settings
$appPlugin = new AppPlugin();
$appPlugin->add_course_settings_form($form);

@ -6,6 +6,7 @@
* Import a backup from moodle system.
*
* @author José Loguercio <jose.loguercio@beeznest.com>
* @author Julio Montoya
*/
require_once __DIR__.'/../inc/global.inc.php';

@ -1,4 +1,5 @@
<?php
/* For licensing terms, see /license.txt */
use Chamilo\CourseBundle\Component\CourseCopy\CourseBuilder;
@ -9,8 +10,6 @@ use Chamilo\CourseBundle\Component\CourseCopy\CourseSelectForm;
* Delete resources from a course.
*
* @author Bart Mollet <bart.mollet@hogent.be>
*
* @package chamilo.backup
*/
require_once __DIR__.'/../inc/global.inc.php';
$current_course_tool = TOOL_COURSE_MAINTENANCE;
@ -70,20 +69,18 @@ if (Security::check_token('post') && (
$recycle_type = 'select_items';
}
$cr = new CourseRecycler($course);
if ($recycle_type == 'full_backup') {
if ($recycle_type === 'full_backup') {
/* to delete, course code confirmation must be equal that current course code */
if ($current_course_code == $courseCodeConfirmation) {
$cr->recycle($recycle_type);
echo Display::return_message(get_lang('RecycleFinished'), 'confirm');
} else {
$messageFailCourseCode = '<p>'.get_lang('CourseRegistrationCodeIncorrect').'</p>';
$messageFailCourseCode .= '<p><a class="btn btn-primary" href="'.api_get_self().'?'.api_get_cidreq().'">'.
echo Display::return_message(get_lang('CourseRegistrationCodeIncorrect'), 'error', false);
echo '<p><a class="btn btn-primary" href="'.api_get_self().'?'.api_get_cidreq().'">'.
get_lang('BackToPreviousPage').
'</a></p>';
echo Display::return_message($messageFailCourseCode, 'error', false);
}
} elseif ($recycle_type == 'select_items') {
} elseif ($recycle_type === 'select_items') {
$cr->recycle($recycle_type);
echo Display::return_message(get_lang('RecycleFinished'), 'confirm');
}
@ -108,25 +105,13 @@ if (Security::check_token('post') && (
} else {
echo Display::return_message(get_lang('RecycleWarning'), 'warning', false);
$form = new FormValidator('recycle_course', 'post', api_get_self().'?'.api_get_cidreq());
$form->addElement('header', get_lang('SelectOptionForBackup'));
$form->addHeader(get_lang('SelectOptionForBackup'));
$form->addElement('radio', 'recycle_option', null, get_lang('FullRecycle'), 'full_backup');
$form->addElement('radio', 'recycle_option', null, get_lang('LetMeSelectItems'), 'select_items');
//Confirmation input code
$form->addElement(
'label',
'',
'<span class="hidden course-full-delete">'.get_lang('CourseCodeConfirmation').'</span>',
null,
null
);
$form->addElement(
'input',
'course_code_confirmation',
null,
'class="hidden course-full-delete"',
'course_code'
);
$form->addHtml('<div class="course-full-delete hidden">');
$form->addText('course_code_confirmation', get_lang('CourseCodeConfirmation'));
$form->addHtml('</div>');
$form->addButtonSave(get_lang('RecycleCourse'));
$form->setDefaults(['recycle_option' => 'select_items']);
@ -137,20 +122,19 @@ if (Security::check_token('post') && (
$form->display();
/* make recycle_course_course_code_confirmation required to put code course */
echo '
<script>
$(function(){
$ (`[type="radio"]`).on (`change`, function (e) {
if ($ (this).val () === `full_backup`) {
$ (`#recycle_course_course_code_confirmation`).prop (`required`, `required`);
$ (`.course-full-delete`).removeClass(`hidden`);
<script>
$(function(){
$(`[type="radio"]`).on(`change`, function (e) {
if ($(this).val() === `full_backup`) {
$(`#recycle_course_course_code_confirmation`).prop(`required`, `required`);
$(`.course-full-delete`).removeClass(`hidden`);
} else {
$ (`#recycle_course_course_code_confirmation`).prop (`required`, ``);
$ (`.course-full-delete`).addClass(`hidden`);
$(`#recycle_course_course_code_confirmation`).prop(`required`, ``);
$(`.course-full-delete`).addClass(`hidden`);
}
});
})
</script>';
})
</script>';
}
}

@ -50,7 +50,7 @@ while ($user = Database::fetch_array($result, 'ASSOC')) {
Database::update($table, $params, ['id = ?' => $userId]);
$extraFields = [
'company' => $ldapUser['company'][0],
'company' => $ldapUser['department'][0],
];
foreach ($extraFields as $variable => $value) {
$params = [
@ -61,7 +61,6 @@ while ($user = Database::fetch_array($result, 'ASSOC')) {
print_r($params).PHP_EOL;
$extraFieldUser->save($params);
}
exit;
}
}
}

@ -16,6 +16,8 @@ $courseInfo = api_get_course_info($courseCode);
if (empty($courseInfo)) {
$courseInfo = api_get_course_info();
}
$type = preg_replace("/[^a-zA-Z_]+/", '', $type);
if (empty($courseInfo) || empty($type) || empty($file)) {
api_not_allowed(true);
}

@ -99,7 +99,8 @@ $is_visible = DocumentManager::check_visibility_tree(
);
if (!$is_allowed_to_edit && !$is_visible) {
api_not_allowed(true);
echo Display::return_message(get_lang('ProtectedDocument'), 'warning');
api_not_allowed(false, '&nbsp;');
}
$pathinfo = pathinfo($header_file);
@ -116,7 +117,6 @@ if (isset($group_id) && $group_id != '') {
if ($current_group) {
$current_group_name = $current_group['name'];
}
$interbreadcrumb[] = [
'url' => api_get_path(WEB_CODE_PATH).'group/group.php?'.api_get_cidreq(),
'name' => get_lang('Groups'),
@ -176,14 +176,14 @@ $frameheight = 135;
if (api_is_course_admin()) {
$frameheight = 165;
}
$execute_iframe = true;
$frameReady = Display::getFrameReadyBlock('#mainFrame');
$web_odf_supported_files = DocumentManager::get_web_odf_extension_list();
// PDF should be displayed with viewerJS
$web_odf_supported_files[] = 'pdf';
if (in_array(strtolower($pathinfo['extension']), $web_odf_supported_files)) {
$show_web_odf = true;
$execute_iframe = false;
$htmlHeadXtra[] = '
<script>
resizeIframe = function() {
@ -194,15 +194,14 @@ if (in_array(strtolower($pathinfo['extension']), $web_odf_supported_files)) {
$(function() {
$(window).resize(resizeIframe());
});
</script>'
;
</script>';
}
// Activate code highlight.
$isChatFolder = false;
if (isset($document_data['parents']) && isset($document_data['parents'][0])) {
$chatFolder = $document_data['parents'][0];
if (isset($chatFolder['path']) && $chatFolder['path'] == '/chat_files') {
if (isset($chatFolder['path']) && $chatFolder['path'] === '/chat_files') {
$isChatFolder = true;
}
}
@ -217,17 +216,12 @@ if ($isChatFolder) {
</script>';
}
$execute_iframe = true;
if ($playerSupported) {
$extension = api_strtolower($pathinfo['extension']);
$execute_iframe = false;
}
if ($show_web_odf) {
$execute_iframe = false;
}
$is_freemind_available = $pathinfo['extension'] == 'mm' && api_get_setting('enable_freemind') == 'true';
$is_freemind_available = $pathinfo['extension'] === 'mm' && api_get_setting('enable_freemind') === 'true';
if ($is_freemind_available) {
$execute_iframe = false;
}
@ -259,22 +253,6 @@ if ($originIsLearnpath) {
$file_url = api_get_path(WEB_COURSE_PATH).$courseInfo['path'].'/document'.$header_file;
$file_url_web = $file_url.'?'.api_get_cidreq();
if ($show_web_odf) {
echo '<div class="text-center">';
$browser = api_get_navigator();
$pdfUrl = api_get_path(WEB_LIBRARY_PATH).'javascript/ViewerJS/index.html#'.$file_url;
if ($browser['name'] == 'Mozilla' && preg_match('|.*\.pdf|i', $header_file)) {
$pdfUrl = $file_url;
}
echo '<div id="viewerJS">';
echo '<iframe id="viewerJSContent" frameborder="0" allowfullscreen="allowfullscreen" webkitallowfullscreen style="width:100%;"
src="'.$pdfUrl.'">
</iframe>';
echo '</div>';
echo '</div>';
}
if ($playerSupported) {
echo DocumentManager::generateMediaPreview($file_url_web, $extension);
}
@ -337,18 +315,13 @@ if ($is_freemind_available) {
//fo.addVariable("toolTipsBgColor","0xaaeeaa");: bgcolor for tooltips ej;"0xaaeeaa"
//fo.addVariable("defaultWordWrap","300"); //default 600
//
fo.write("flashcontent");
// ]]>
</script>
<?php
}
if ($execute_iframe) {
if ($isChatFolder) {
$content = Security::remove_XSS(file_get_contents($file_url_sys));
echo $content;
} else {
if (($execute_iframe || $show_web_odf) && !$isChatFolder) {
$parentId = $document_data['parent_id'];
$url = api_get_path(WEB_CODE_PATH).'document/document.php?'.api_get_cidreq().'&id='.$parentId;
$actionsLeft = Display::url(
@ -371,8 +344,8 @@ if ($execute_iframe) {
}
$allowToEdit = api_is_allowed_to_edit(null, true) || $groupMemberWithEditRights;
if ($allowToEdit) {
if (false === $show_web_odf) {
$actionsLeft .= Display::url(
Display::return_icon(
'edit.png',
@ -382,9 +355,9 @@ if ($execute_iframe) {
),
api_get_path(WEB_CODE_PATH).'document/edit_document.php?'.api_get_cidreq().'&id='.$document_id
);
}
$titleToShow = addslashes(basename($document_data['title']));
$urlDeleteParams = http_build_query(
[
'action' => 'delete_item',
@ -392,26 +365,55 @@ if ($execute_iframe) {
'deleteid' => $document_data['id'],
]
);
$actionsLeft .= Display::url(
Display::return_icon('delete.png', get_lang('Delete'), '', ICON_SIZE_MEDIUM),
'#',
[
'data-item-title' => $titleToShow,
'data-href' => api_get_path(WEB_CODE_PATH).'document/document.php?'.api_get_cidreq(
).'&'.$urlDeleteParams,
'data-href' => api_get_path(WEB_CODE_PATH).'document/document.php?'.api_get_cidreq().'&'.$urlDeleteParams,
'data-toggle' => 'modal',
'data-target' => '#confirm-delete',
]
);
if (false === $show_web_odf) {
$actionsLeft .= Display::url(
Display::return_icon('pdf.png', get_lang('Export2PDF'), [], ICON_SIZE_MEDIUM),
api_get_path(WEB_CODE_PATH).'document/document.php?'.api_get_cidreq(
).'&action=export_to_pdf&id='.$document_id
);
}
}
echo $toolbar = Display::toolbarAction('actions-documents', [$actionsLeft]);
}
if ($show_web_odf) {
$execute_iframe = false;
echo '<div class="text-center">';
$browser = api_get_navigator();
$pdfUrl = api_get_path(WEB_LIBRARY_PATH).'javascript/ViewerJS/index.html?zoom=page-width#'.$file_url;
if ($browser['name'] === 'Mozilla' && preg_match('|.*\.pdf|i', $header_file)) {
$pdfUrl = $file_url;
}
echo '<div id="viewerJS">';
echo '<iframe
id="viewerJSContent"
frameborder="0"
allowfullscreen="allowfullscreen"
webkitallowfullscreen
style="width:100%;height:600px;"
src="'.$pdfUrl.'">
</iframe>';
echo '</div>';
echo '</div>';
}
if ($execute_iframe) {
if ($isChatFolder) {
$content = Security::remove_XSS(file_get_contents($file_url_sys));
echo $content;
} else {
echo '<iframe
id="mainFrame"
name="mainFrame"

@ -95,6 +95,8 @@ class Exercise
public $hideComment;
public $hideNoAnswer;
public $hideExpectedAnswer;
public $forceShowExpectedChoiceColumn;
public $disableHideCorrectAnsweredQuestions;
/**
* Constructor of the class.
@ -139,6 +141,7 @@ class Exercise
$this->hideComment = false;
$this->hideNoAnswer = false;
$this->hideExpectedAnswer = false;
$this->disableHideCorrectAnsweredQuestions = false;
if (!empty($courseId)) {
$courseInfo = api_get_course_info_by_id($courseId);
@ -2205,6 +2208,12 @@ class Exercise
null,
get_lang('HideCategoryTable')
),
$form->createElement(
'checkbox',
'hide_correct_answered_questions',
null,
get_lang('HideCorrectAnsweredQuestions')
),
];
$form->addGroup($group, null, get_lang('ResultsConfigurationPage'));
}
@ -2616,6 +2625,11 @@ class Exercise
{
// Feedback type.
$feedback = [];
$warning = sprintf(
get_lang('TheSettingXWillChangeToX'),
get_lang('ShowResultsToStudents'),
get_lang('ShowScoreAndRightAnswer')
);
$endTest = $form->createElement(
'radio',
'exerciseFeedbackType',
@ -2624,7 +2638,8 @@ class Exercise
EXERCISE_FEEDBACK_TYPE_END,
[
'id' => 'exerciseType_'.EXERCISE_FEEDBACK_TYPE_END,
'onclick' => 'check_feedback()',
//'onclick' => 'if confirm() check_feedback()',
'onclick' => 'javascript:if(confirm('."'".addslashes($warning)."'".')) { check_feedback(); } else { return false;} ',
]
);
@ -3320,8 +3335,6 @@ class Exercise
}
$isReviewingAnswers = isset($_REQUEST['reminder']) && 2 === (int) $_REQUEST['reminder'];
// User
$endReminderValue = false;
if (!empty($myRemindList) && $isReviewingAnswers) {
$endValue = end($myRemindList);
@ -3329,11 +3342,13 @@ class Exercise
$endReminderValue = true;
}
}
$endTest = false;
if ($this->type == ALL_ON_ONE_PAGE || $nbrQuestions == $questionNum || $endReminderValue) {
if ($this->review_answers) {
$label = get_lang('ReviewQuestions');
$class = 'btn btn-success';
} else {
$endTest = true;
$label = get_lang('EndTest');
$class = 'btn btn-warning';
}
@ -3396,10 +3411,15 @@ class Exercise
]
);
} else {
$attributes = ['type' => 'button', 'class' => $class, 'data-question' => $question_id];
$name = 'save_now';
if ($endTest && api_get_configuration_value('quiz_check_all_answers_before_end_test')) {
$name = 'check_answers';
}
$buttonList[] = Display::button(
'save_now',
$name,
$label,
['type' => 'button', 'class' => $class, 'data-question' => $question_id]
$attributes
);
}
$buttonList[] = '<span id="save_for_now_'.$question_id.'" class="exercise_save_mini_message"></span>';
@ -3713,7 +3733,6 @@ class Exercise
if ($answerType == ORAL_EXPRESSION) {
$exe_info = Event::get_exercise_results_by_attempt($exeId);
$exe_info = isset($exe_info[$exeId]) ? $exe_info[$exeId] : null;
$objQuestionTmp->initFile(
api_get_session_id(),
isset($exe_info['exe_user_id']) ? $exe_info['exe_user_id'] : api_get_user_id(),
@ -3843,7 +3862,6 @@ class Exercise
$option = isset($values[1]) ? $values[1] : '';
$choice[$my_answer_id] = $option;
}
$userAnsweredQuestion = !empty($choice);
}
@ -4451,14 +4469,13 @@ class Exercise
$result = Database::query($sql);
$data = Database::fetch_array($result);
$choice = '';
$questionScore = 0;
if ($data) {
$choice = $data['answer'];
$questionScore = $data['marks'];
}
$choice = str_replace('\r\n', '', $choice);
$choice = stripslashes($choice);
$questionScore = $data['marks'];
if (-1 == $questionScore) {
$totalScore += 0;
} else {
@ -4924,7 +4941,7 @@ class Exercise
$coords = array_filter($coords);
$user_array = '';
foreach ($coords as $coord) {
list($x, $y) = explode(';', $coord);
[$x, $y] = explode(';', $coord);
$user_array .= round($x).';'.round($y).'/';
}
$user_array = substr($user_array, 0, -1) ?: '';
@ -7749,7 +7766,7 @@ class Exercise
}
}
$attributes = ['id' => 'remind_list['.$questionId.']'];
$attributes = ['id' => 'remind_list['.$questionId.']', 'data-question-id' => $questionId];
// Showing the question
$exercise_actions = null;
@ -8323,10 +8340,11 @@ class Exercise
$pageConfig = api_get_configuration_value('allow_quiz_results_page_config');
if ($pageConfig) {
$params = [
'hide_expected_answer' => isset($values['hide_expected_answer']) ? $values['hide_expected_answer'] : '',
'hide_question_score' => isset($values['hide_question_score']) ? $values['hide_question_score'] : '',
'hide_total_score' => isset($values['hide_total_score']) ? $values['hide_total_score'] : '',
'hide_category_table' => isset($values['hide_category_table']) ? $values['hide_category_table'] : '',
'hide_expected_answer' => $values['hide_expected_answer'] ?? '',
'hide_question_score' => $values['hide_question_score'] ?? '',
'hide_total_score' => $values['hide_total_score'] ?? '',
'hide_category_table' => $values['hide_category_table'] ?? '',
'hide_correct_answered_questions' => $values['hide_correct_answered_questions'] ?? '',
];
$type = Type::getType('array');
$platform = Database::getManager()->getConnection()->getDatabasePlatform();
@ -8418,6 +8436,10 @@ class Exercise
*/
public function showExpectedChoiceColumn()
{
if (true === $this->forceShowExpectedChoiceColumn) {
return true;
}
if ($this->hideExpectedAnswer) {
return false;
}
@ -10361,11 +10383,12 @@ class Exercise
return $lpList;
}
public function getReminderTable($questionList, $exercise_stat_info)
public function getReminderTable($questionList, $exercise_stat_info, $disableCheckBoxes = false)
{
$learnpath_id = isset($_REQUEST['learnpath_id']) ? (int) $_REQUEST['learnpath_id'] : 0;
$learnpath_item_id = isset($_REQUEST['learnpath_item_id']) ? (int) $_REQUEST['learnpath_item_id'] : 0;
$learnpath_item_view_id = isset($_REQUEST['learnpath_item_view_id']) ? (int) $_REQUEST['learnpath_item_view_id'] : 0;
$categoryId = isset($_REQUEST['category_id']) ? (int) $_REQUEST['category_id'] : 0;
if (empty($exercise_stat_info)) {
return '';
@ -10373,7 +10396,6 @@ class Exercise
$remindList = $exercise_stat_info['questions_to_check'];
$remindList = explode(',', $remindList);
$exeId = $exercise_stat_info['exe_id'];
$exerciseId = $exercise_stat_info['exe_exo_id'];
$exercise_result = $this->getUserAnswersSavedInExercise($exeId);
@ -10386,7 +10408,11 @@ class Exercise
foreach ($questionList as $questionId) {
$objQuestionTmp = Question::read($questionId);
$check_id = 'remind_list['.$questionId.']';
$attributes = ['id' => $check_id, 'onclick' => "save_remind_item(this, '$questionId');"];
$attributes = [
'id' => $check_id,
'onclick' => "save_remind_item(this, '$questionId');",
'data-question-id' => $questionId,
];
if (in_array($questionId, $remindList)) {
$attributes['checked'] = 1;
}
@ -10407,10 +10433,11 @@ class Exercise
if (!in_array($questionId, $exercise_result)) {
$questionTitle = Display::label($questionTitle, 'danger');
}
$label_attributes = [];
$label_attributes['for'] = $check_id;
if (false === $disableCheckBoxes) {
$questionTitle = Display::tag('label', $checkbox.$questionTitle, $label_attributes);
}
$table .= Display::div($questionTitle, ['class' => 'exercise_reminder_item ']);
}
@ -10429,26 +10456,39 @@ class Exercise
window.location = "'.api_get_path(WEB_CODE_PATH).'exercise/exercise_result.php?'.api_get_cidreq().'&exe_id='.$exeId.'&" + lp_data;
}
function selectAll() {
$("input[type=checkbox]").each(function () {
$(this).prop("checked", 1);
var question_id = $(this).data("question-id");
var action = "add";
$.ajax({
url: "'.api_get_path(WEB_AJAX_PATH).'exercise.ajax.php?'.api_get_cidreq().'&a=add_question_to_reminder",
data: "question_id="+question_id+"&exe_id='.$exeId.'&action="+action,
success: function(returnValue) {
}
});
});
}
function changeOptionStatus(status)
{
$("input[type=checkbox]").each(function () {
$(this).prop("checked", status);
});
var action = "";
var extraOption = "remove_all";
var option = "remove_all";
if (status == 1) {
extraOption = "add_all";
option = "add_all";
}
$.ajax({
url: "'.api_get_path(WEB_AJAX_PATH).'exercise.ajax.php?'.api_get_cidreq().'&a=add_question_to_reminder",
data: "option="+extraOption+"&exe_id='.$exeId.'&action="+action,
data: "option="+option+"&exe_id='.$exeId.'&action="+action,
success: function(returnValue) {
}
});
}
function review_questions() {
function reviewQuestions() {
var isChecked = 1;
$("input[type=checkbox]").each(function () {
if ($(this).prop("checked")) {
@ -10461,7 +10501,7 @@ class Exercise
$("#message").addClass("warning-message");
$("#message").html("'.addslashes(get_lang('SelectAQuestionToReview')).'");
} else {
window.location = "exercise_submit.php?'.api_get_cidreq().'&exerciseId='.$exerciseId.'&reminder=2&" + lp_data;
window.location = "exercise_submit.php?'.api_get_cidreq().'&category_id='.$categoryId.'&exerciseId='.$exerciseId.'&reminder=2&" + lp_data;
}
}

@ -14,7 +14,6 @@ if (false === api_get_configuration_value('block_category_questions')) {
$this_section = SECTION_COURSES;
api_protect_course_script(true);
$origin = api_get_origin();
$learnpath_id = isset($_REQUEST['learnpath_id']) ? (int) $_REQUEST['learnpath_id'] : 0;
$learnpath_item_id = isset($_REQUEST['learnpath_item_id']) ? (int) $_REQUEST['learnpath_item_id'] : 0;
$learnpath_item_view_id = isset($_REQUEST['learnpath_item_view_id']) ? (int) $_REQUEST['learnpath_item_view_id'] : 0;
@ -38,10 +37,11 @@ if (empty($objExercise) || empty($questionCategoryId) || empty($exeId) || empty(
api_not_allowed(true);
}
$categoryId = $categoryObj->id;
$categoryId = (int) $categoryObj->id;
$params = "exe_id=$exeId&exerciseId=$exerciseId&learnpath_id=$learnpath_id&learnpath_item_id=$learnpath_item_id&learnpath_item_view_id=$learnpath_item_view_id&".api_get_cidreq();
$url = api_get_path(WEB_CODE_PATH).'exercise/exercise_submit.php?'.$params;
$validateUrl = api_get_self().'?'.$params.'&category_id='.$categoryId.'&validate=1';
$validateUrl = api_get_path(WEB_CODE_PATH).'exercise/exercise_question_reminder.php?'.
$params.'&category_id='.$categoryId.'&validate=1';
$time_control = false;
$clock_expired_time = ExerciseLib::get_session_time_control_key(
@ -88,7 +88,6 @@ if ($validateCategory) {
// Cleaning old remind list.
$objExercise->removeAllQuestionToRemind($exeId);
api_location($url.'&num='.$currentQuestion);
}
@ -103,11 +102,11 @@ if (!$hideHeaderAndFooter) {
}
// I'm in a preview mode as course admin. Display the action menu.
if (api_is_course_admin() && !$hideHeaderAndFooter) {
if (!$hideHeaderAndFooter && api_is_course_admin()) {
echo '<div class="actions">';
echo '<a href="admin.php?'.api_get_cidreq().'&exerciseId='.$objExercise->id.'">'.
echo '<a href="admin.php?'.api_get_cidreq().'&exerciseId='.$objExercise->iId.'">'.
Display::return_icon('back.png', get_lang('GoBackToQuestionList'), [], 32).'</a>';
echo '<a href="exercise_admin.php?'.api_get_cidreq().'&modifyExercise=yes&exerciseId='.$objExercise->id.'">'.
echo '<a href="exercise_admin.php?'.api_get_cidreq().'&modifyExercise=yes&exerciseId='.$objExercise->iId.'">'.
Display::return_icon('edit.png', get_lang('ModifyExercise'), [], 32).'</a>';
echo '</div>';
}
@ -115,12 +114,14 @@ echo Display::page_header($categoryObj->name);
echo '<p>'.Security::remove_XSS($categoryObj->description).'</p>';
echo '<p>'.get_lang('BlockCategoryExplanation').'</p>';
if ($objExercise->review_answers) {
$questionList = [];
$categoryList = Session::read('categoryList');
if (isset($categoryList[$categoryId])) {
$categoryList = Session::read('categoryList');
$disableAllQuestions = '';
$questionList = [];
if (isset($categoryList[$categoryId])) {
$questionList = $categoryList[$categoryId];
}
}
if ($objExercise->review_answers) {
$disableAllQuestions = 'changeOptionStatus(0);';
echo $objExercise->getReminderTable($questionList, $trackInfo);
}
@ -130,19 +131,28 @@ if ($time_control) {
echo Display::div('', ['id' => 'message']);
$previousQuestion = $currentQuestion - 1;
echo '<script>
var lp_data = $.param({
"learnpath_id": '.$learnpath_id.',
"learnpath_item_id" : '.$learnpath_item_id.',
"learnpath_item_view_id": '.$learnpath_item_view_id.'
});
$nextQuestion = $currentQuestion + 1;
if (!empty($questionList)) {
$firstQuestionOfCategory = end($questionList);
$dataTracking = explode(',', $trackInfo['data_tracking']);
$index = 0;
foreach ($dataTracking as $index => $question) {
if ($firstQuestionOfCategory == $question) {
break;
}
}
$nextQuestion = $index + 1;
}
echo '<script>
function goBack() {
window.location = "'.$url.'&num='.$previousQuestion.'&" + lp_data;
window.location = "'.$url.'&num='.$previousQuestion.'";
}
function continueExercise() {
window.location = "'.$validateUrl.'&num='.$currentQuestion.'&" + lp_data;
'.$disableAllQuestions.'
window.location = "'.$validateUrl.'&num='.$nextQuestion.'";
}
function final_submit() {
@ -164,17 +174,43 @@ if (!in_array($categoryId, $blockedCategories)) {
['onclick' => 'goBack();', 'class' => 'btn btn-default']
);
}
$exerciseActions .= '&nbsp;'.Display::url(
get_lang('ContinueTest'),
if ($objExercise->review_answers) {
$exerciseActions .= Display::url(
get_lang('ReviewQuestions'),
'javascript://',
['onclick' => 'continueExercise();', 'class' => 'btn btn-primary']
);
/*
$exerciseActions .= '&nbsp;'.Display::url(
['onclick' => 'reviewQuestions();', 'class' => 'btn btn-primary']
);
$exerciseActions .= '&nbsp;'.Display::url(
get_lang('SelectAll'),
'javascript://',
['onclick' => 'selectAll();', 'class' => 'btn btn-default']
);
$exerciseActions .= '&nbsp;'.Display::url(
get_lang('UnSelectAll'),
'javascript://',
['onclick' => 'changeOptionStatus(0);', 'class' => 'btn btn-default']
);
}
end($categoryList);
// This is the last category
if (key($categoryList) === $categoryId) {
$exerciseActions .= '&nbsp;'.Display::url(
get_lang('EndTest'),
'javascript://',
['onclick' => 'final_submit();', 'class' => 'btn btn-warning']
);*/
);
} else {
$exerciseActions .= '&nbsp;'.Display::url(
get_lang('ContinueTest'),
'javascript://',
['onclick' => 'continueExercise();', 'class' => 'btn btn-primary']
);
}
echo Display::div('', ['class' => 'clear']);
echo Display::div($exerciseActions, ['class' => 'form-actions']);

@ -82,14 +82,13 @@ $interbreadcrumb[] = ['url' => 'exercise.php?'.api_get_cidreq(), 'name' => get_l
$hideHeaderAndFooter = in_array($origin, ['learnpath', 'embeddable']);
if (!$hideHeaderAndFooter) {
//so we are not in learnpath tool
Display::display_header($nameTools, get_lang('Exercise'));
} else {
Display::display_reduced_header();
}
// I'm in a preview mode as course admin. Display the action menu.
if (api_is_course_admin() && !$hideHeaderAndFooter) {
if (!$hideHeaderAndFooter && api_is_course_admin()) {
echo '<div class="actions">';
echo '<a href="admin.php?'.api_get_cidreq().'&exerciseId='.$objExercise->id.'">'.
Display::return_icon('back.png', get_lang('GoBackToQuestionList'), [], 32).'</a>';
@ -122,7 +121,7 @@ echo $objExercise->getReminderTable($question_list, $exercise_stat_info);
$exerciseActions = Display::url(
get_lang('ReviewQuestions'),
'javascript://',
['onclick' => 'review_questions();', 'class' => 'btn btn-primary']
['onclick' => 'reviewQuestions();', 'class' => 'btn btn-primary']
);
$exerciseActions .= '&nbsp;'.Display::url(

@ -18,7 +18,7 @@ $htmlHeadXtra[] = api_get_jqgrid_js();
$filter_user = isset($_REQUEST['filter_by_user']) ? (int) $_REQUEST['filter_by_user'] : null;
$isBossOfStudent = false;
if (api_is_student_boss() && !empty($filter_user)) {
if (!empty($filter_user) && api_is_student_boss()) {
// Check if boss has access to user info.
if (UserManager::userIsBossOfStudent(api_get_user_id(), $filter_user)) {
$isBossOfStudent = true;
@ -157,7 +157,6 @@ if (isset($_REQUEST['comments']) &&
// Filtered by post-condition
$id = (int) $_GET['exeid'];
$track_exercise_info = ExerciseLib::get_exercise_track_exercise_info($id);
if (empty($track_exercise_info)) {
api_not_allowed();
}
@ -168,54 +167,73 @@ if (isset($_REQUEST['comments']) &&
$lp_item_view_id = (int) $track_exercise_info['orig_lp_item_view_id'];
$exerciseId = $track_exercise_info['exe_exo_id'];
$exeWeighting = $track_exercise_info['exe_weighting'];
$attemptData = Event::get_exercise_results_by_attempt($id);
$questionListData = [];
if ($attemptData && $attemptData[$id] && $attemptData[$id]['question_list']) {
$questionListData = $attemptData[$id]['question_list'];
}
$post_content_id = [];
$comments_exist = false;
$questionListInPost = [];
foreach ($_POST as $key_index => $key_value) {
$my_post_info = explode('_', $key_index);
$post_content_id[] = isset($my_post_info[1]) ? $my_post_info[1] : null;
if ($my_post_info[0] === 'comments') {
$comments_exist = true;
$questionListInPost[] = $my_post_info[1];
}
}
$loop_in_track = $comments_exist === true ? (count($_POST) / 2) : count($_POST);
if ($comments_exist === true) {
$array_content_id_exe = array_slice($post_content_id, $loop_in_track);
} else {
$array_content_id_exe = $post_content_id;
}
for ($i = 0; $i < $loop_in_track; $i++) {
$my_marks = isset($_POST['marks_'.$array_content_id_exe[$i]]) ? $_POST['marks_'.$array_content_id_exe[$i]] : '';
$my_comments = '';
if (isset($_POST['comments_'.$array_content_id_exe[$i]])) {
$my_comments = $_POST['comments_'.$array_content_id_exe[$i]];
}
$my_questionid = (int) $array_content_id_exe[$i];
foreach ($questionListInPost as $questionId) {
$marks = $_POST['marks_'.$questionId] ?? 0;
$my_comments = $_POST['comments_'.$questionId] ?? '';
$params = [
'marks' => $my_marks,
'teacher_comment' => $my_comments,
];
$question = Question::read($questionId);
if (false === $question) {
continue;
}
// From the database.
$marksFromDatabase = $questionListData[$questionId]['marks'];
if (in_array($question->type, [FREE_ANSWER, ORAL_EXPRESSION, ANNOTATION])) {
// From the form.
$params['marks'] = $marks;
if ($marksFromDatabase != $marks) {
Event::addEvent(
LOG_QUESTION_SCORE_UPDATE,
LOG_EXERCISE_ATTEMPT_QUESTION_ID,
[
'exe_id' => $id,
'question_id' => $questionId,
'old_marks' => $marksFromDatabase,
'new_marks' => $marks,
]
);
}
} else {
$marks = $marksFromDatabase;
}
Database::update(
$TBL_TRACK_ATTEMPT,
$params,
['question_id = ? AND exe_id = ?' => [$my_questionid, $id]]
['question_id = ? AND exe_id = ?' => [$questionId, $id]]
);
$params = [
'exe_id' => $id,
'question_id' => $my_questionid,
'marks' => $my_marks,
'question_id' => $questionId,
'marks' => $marks,
'insert_date' => api_get_utc_datetime(),
'author' => api_get_user_id(),
'teacher_comment' => $my_comments,
];
Database::insert($TBL_TRACK_ATTEMPT_RECORDING, $params);
}
$useEvaluationPlugin = false;
$pluginEvaluation = QuestionOptionsEvaluationPlugin::create();
@ -251,14 +269,12 @@ if (isset($_REQUEST['comments']) &&
//@todo move this somewhere else
$subject = get_lang('ExamSheetVCC');
$message = isset($_POST['notification_content']) ? $_POST['notification_content'] : '';
MessageManager::send_message_simple(
$student_id,
$subject,
$message,
api_get_user_id()
);
if ($allowCoachFeedbackExercises) {
Display::addFlash(
Display::return_message(get_lang('MessageSent'))
@ -268,6 +284,9 @@ if (isset($_REQUEST['comments']) &&
$notifications = api_get_configuration_value('exercise_finished_notification_settings');
if ($notifications) {
$oldResultDisabled = $objExerciseTmp->results_disabled;
$objExerciseTmp->results_disabled = RESULT_DISABLE_SHOW_SCORE_AND_EXPECTED_ANSWERS;
ob_start();
$stats = ExerciseLib::displayQuestionListByAttempt(
$objExerciseTmp,
@ -278,7 +297,27 @@ if (isset($_REQUEST['comments']) &&
api_get_configuration_value('quiz_results_answers_report'),
false
);
$objExerciseTmp->results_disabled = $oldResultDisabled;
ob_end_clean();
// Show all for teachers.
$oldResultDisabled = $objExerciseTmp->results_disabled;
$objExerciseTmp->results_disabled = RESULT_DISABLE_SHOW_SCORE_AND_EXPECTED_ANSWERS;
$objExerciseTmp->forceShowExpectedChoiceColumn = true;
ob_start();
$statsTeacher = ExerciseLib::displayQuestionListByAttempt(
$objExerciseTmp,
$track_exercise_info['exe_id'],
false,
false,
false,
api_get_configuration_value('quiz_results_answers_report'),
false
);
ob_end_clean();
$objExerciseTmp->forceShowExpectedChoiceColumn = false;
$objExerciseTmp->results_disabled = $oldResultDisabled;
$attemptCount = Event::getAttemptPosition(
$track_exercise_info['exe_id'],
@ -295,7 +334,8 @@ if (isset($_REQUEST['comments']) &&
$track_exercise_info,
api_get_course_info(),
$attemptCount,
$stats
$stats,
$statsTeacher
);
}

@ -60,6 +60,12 @@ class ExerciseResult
$session_id_and = ' AND te.session_id = '.$sessionId.' ';
$exercise_id = (int) $exercise_id;
if (empty($sessionId) &&
api_get_configuration_value('show_exercise_session_attempts_in_base_course')
) {
$session_id_and = '';
}
if (!empty($exercise_id)) {
$session_id_and .= " AND exe_exo_id = $exercise_id ";
}

@ -82,6 +82,7 @@ if (!empty($objExercise->getResultAccess())) {
$showHeader = false;
$showFooter = false;
$showLearnPath = true;
$pageActions = '';
$pageTop = '';
$pageBottom = '';
@ -90,6 +91,7 @@ $courseInfo = api_get_course_info();
if (!in_array($origin, ['learnpath', 'embeddable', 'mobileapp'])) {
// So we are not in learnpath tool
$showHeader = true;
$showLearnPath = false;
}
// I'm in a preview mode as course admin. Display the action menu.
@ -202,7 +204,7 @@ if (!empty($exercise_stat_info)) {
$max_score = $objExercise->get_max_score();
if ($origin === 'embeddable') {
if ('embeddable' === $origin) {
$pageTop .= showEmbeddableFinishButton();
} else {
Display::addFlash(
@ -224,6 +226,30 @@ $stats = ExerciseLib::displayQuestionListByAttempt(
);
$pageContent .= ob_get_contents();
ob_end_clean();
// Change settings for teacher access.
$oldResultDisabled = $objExercise->results_disabled;
$objExercise->results_disabled = RESULT_DISABLE_SHOW_SCORE_AND_EXPECTED_ANSWERS;
$objExercise->forceShowExpectedChoiceColumn = true;
$objExercise->disableHideCorrectAnsweredQuestions = true;
ob_start();
$statsTeacher = ExerciseLib::displayQuestionListByAttempt(
$objExercise,
$exeId,
false,
$remainingMessage,
$allowSignature,
api_get_configuration_value('quiz_results_answers_report'),
false
);
ob_end_clean();
// Restore settings.
$objExercise->results_disabled = $oldResultDisabled;
$objExercise->forceShowExpectedChoiceColumn = false;
$objExercise->disableHideCorrectAnsweredQuestions = false;
// Save here LP status
if (!empty($learnpath_id) && $saveResults) {
// Save attempt in lp
@ -236,7 +262,8 @@ ExerciseLib::sendNotification(
$exercise_stat_info,
$courseInfo,
$attempt_count++,
$stats
$stats,
$statsTeacher
);
$hookQuizEnd = HookQuizEnd::create();
@ -289,7 +316,7 @@ if (!in_array($origin, ['learnpath', 'embeddable', 'mobileapp'])) {
$showFooter = false;
}
$template = new Template($nameTools, $showHeader, $showFooter);
$template = new Template($nameTools, $showHeader, $showFooter, $showLearnPath);
$template->assign('page_top', $pageTop);
$template->assign('page_content', $pageContent);
$template->assign('page_bottom', $pageBottom);

@ -402,6 +402,11 @@ $arrmarks = [];
$strids = '';
$marksid = '';
$countPendingQuestions = 0;
$audioTemplate = null;
if ($allowRecordAudio && $allowTeacherCommentAudio) {
$audioTemplate = new Template('', false, false, false, false, false, false);
}
foreach ($questionList as $questionId) {
$choice = isset($exerciseResult[$questionId]) ? $exerciseResult[$questionId] : '';
// destruction of the Question object
@ -635,8 +640,6 @@ foreach ($questionList as $questionId) {
$renderer = &$feedback_form->defaultRenderer();
$renderer->setFormTemplate('<form{attributes}><div>{content}</div></form>');
$renderer->setCustomElementTemplate('<div>{element}</div>');
$comnt = Event::get_comments($id, $questionId);
$textareaId = 'comments_'.$questionId;
$default = [$textareaId => $comnt];
@ -657,12 +660,11 @@ foreach ($questionList as $questionId) {
}
$feedback_form->setDefaults($default);
$feedback_form->display();
echo '</div>';
if ($allowRecordAudio && $allowTeacherCommentAudio) {
echo '<div class="col-sm-5">';
echo ExerciseLib::getOralFeedbackForm($id, $questionId, $student_id);
echo ExerciseLib::getOralFeedbackForm($audioTemplate, $id, $questionId, $student_id);
echo '</div>';
}
echo '</div>';

@ -127,10 +127,8 @@ $exerciseResult = isset($_REQUEST['exerciseResult']) ? $_REQUEST['exerciseResult
$exerciseResultCoordinates = isset($_REQUEST['exerciseResultCoordinates']) ? $_REQUEST['exerciseResultCoordinates'] : null;
$choice = isset($_REQUEST['choice']) ? $_REQUEST['choice'] : null;
$choice = empty($choice) ? isset($_REQUEST['choice2']) ? $_REQUEST['choice2'] : null : null;
$questionCategoryId = isset($_REQUEST['category_id']) ? (int) $_REQUEST['category_id'] : 0;
$current_question = $currentQuestionFromUrl = isset($_REQUEST['num']) ? (int) $_REQUEST['num'] : null;
$currentAnswer = isset($_REQUEST['num_answer']) ? (int) $_REQUEST['num_answer'] : null;
$logInfo = [
'tool' => TOOL_QUIZ,
'tool_id' => $exerciseId,
@ -176,7 +174,7 @@ if (empty($exerciseInSession) || (!empty($exerciseInSession) && $exerciseInSessi
} else {
Session::write('firstTime', false);
}
//2. Checking if $objExercise is set.
// 2. Checking if $objExercise is set.
/** @var |Exercise $objExercise */
if (!isset($objExercise) && isset($exerciseInSession)) {
if ($debug) {
@ -434,12 +432,13 @@ if (empty($exercise_stat_info)) {
$exe_id = $exercise_stat_info['exe_id'];
// Remember last question id position.
$isFirstTime = Session::read('firstTime');
//$isFirstTime = true;
if ($isFirstTime && ONE_PER_PAGE == $objExercise->type) {
$resolvedQuestions = Event::getAllExerciseEventByExeId($exe_id);
if (!empty($resolvedQuestions) &&
!empty($exercise_stat_info['data_tracking'])
) {
$last = current(end($resolvedQuestions));
/*$last = current(end($resolvedQuestions));
$attemptQuestionList = explode(',', $exercise_stat_info['data_tracking']);
$count = 1;
foreach ($attemptQuestionList as $question) {
@ -449,10 +448,26 @@ if (empty($exercise_stat_info)) {
$count++;
}
$current_question = $count;
*/
// Get current question based in data_tracking question list, instead of track_e_attempt order BT#17789.
$resolvedQuestionsQuestionIds = array_keys($resolvedQuestions);
$count = 0;
$attemptQuestionList = explode(',', $exercise_stat_info['data_tracking']);
//var_dump($attemptQuestionList, $resolvedQuestionsQuestionIds);
foreach ($attemptQuestionList as $index => $question) {
if (in_array($question, $resolvedQuestionsQuestionIds)) {
$count = $index;
continue;
}
}
$current_question = $count;
//var_dump($current_question, $index);exit;
}
}
}
Session::write('exe_id', $exe_id);
$checkAnswersUrl = api_get_path(WEB_AJAX_PATH).'exercise.ajax.php?a=check_answers&exe_id='.$exe_id.'&'.api_get_cidreq();
$saveDurationUrl = api_get_path(WEB_AJAX_PATH).'exercise.ajax.php?a=update_duration&exe_id='.$exe_id.'&'.api_get_cidreq();
$questionListInSession = Session::read('questionList');
$selectionType = $objExercise->getQuestionSelectionType();
@ -508,7 +523,7 @@ if (!empty($exercise_stat_info['questions_to_check'])) {
}
$params = "exe_id=$exe_id&exerciseId=$exerciseId&learnpath_id=$learnpath_id&learnpath_item_id=$learnpath_item_id&learnpath_item_view_id=$learnpath_item_view_id&".api_get_cidreq().'&reminder='.$reminder;
if (2 == $reminder && empty($myRemindList)) {
if (2 === $reminder && empty($myRemindList)) {
if ($debug) {
error_log('6.2 calling the exercise_reminder.php');
}
@ -598,7 +613,6 @@ if ($time_control) {
//Sends the exercise form when the expired time is finished.
$htmlHeadXtra[] = $objExercise->showTimeControlJS($time_left);
}
// in LP's is enabled the "remember question" feature?
if (!isset($_SESSION['questionList'])) {
// selects the list of question ID
@ -657,43 +671,55 @@ if ($allowBlockCategory &&
}
$count++;
}
//var_dump($questionCheck);exit;
// Use reminder list to get the current question.
if (2 === $reminder && !empty($myRemindList)) {
/*if (2 === $reminder && !empty($myRemindList)) {
$remindQuestionId = current($myRemindList);
$questionCheck = Question::read($remindQuestionId);
}
}*/
$categoryId = 0;
if (null !== $questionCheck) {
$categoryId = $questionCheck->category;
}
if ($objExercise->review_answers && isset($_GET['category_id'])) {
$categoryId = $_GET['category_id'] ?? 0;
}
//var_dump($categoryId, $categoryList);
if (!empty($categoryId)) {
$categoryInfo = $categoryList[$categoryId];
$count = 1;
$total = count($categoryList[$categoryId]);
//var_dump($questionCheck);
foreach ($categoryList[$categoryId] as $checkQuestionId) {
if ((int) $checkQuestionId === $questionCheck->iid) {
if ((int) $checkQuestionId === (int) $questionCheck->iid) {
break;
}
$count++;
}
//var_dump($count , $total);
if ($count === $total) {
$isLastQuestionInCategory = $categoryId;
if ($isLastQuestionInCategory) {
// This is the last question
if ((int) $current_question + 1 === count($questionList)) {
if (false === $objExercise->review_answers) {
$isLastQuestionInCategory = 0;
}
}
}
}
if (0 === $isLastQuestionInCategory) {
$showPreviousButton = false;
}
if (0 === $isLastQuestionInCategory && 2 === $reminder) {
// $isLastQuestionInCategory = $categoryId;
}
}
//var_dump($categoryId, $blockedCategories, $isLastQuestionInCategory);
// Blocked if category was already answered.
if ($categoryId && in_array($categoryId, $blockedCategories)) {
@ -703,7 +729,8 @@ if ($allowBlockCategory &&
api_location($url);
}
}
//exit;
//var_dump($isLastQuestionInCategory);
if ($debug) {
error_log('8. Question list loaded '.print_r($questionList, 1));
}
@ -713,7 +740,7 @@ $question_count = 0;
if (!empty($questionList)) {
$question_count = count($questionList);
}
//var_dump($current_question);
if ($current_question > $question_count) {
// If time control then don't change the current question, otherwise there will be a loop.
// @todo
@ -757,7 +784,7 @@ if ($formSent && isset($_POST)) {
$exerciseResult = $choice;
} else {
// gets the question ID from $choice. It is the key of the array
list($key) = array_keys($choice);
[$key] = array_keys($choice);
// if the user didn't already answer this question
if (!isset($exerciseResult[$key])) {
// stores the user answer into the array
@ -964,7 +991,6 @@ if ($allowTimePerQuestion && $objExercise->type == ONE_PER_PAGE) {
$previousQuestion = $objQuestionTmp;
}
}
$extraFieldValue = new ExtraFieldValue('question');
$value = $extraFieldValue->get_values_by_handler_and_field_variable($objQuestionTmp->iid, 'time');
if (!empty($value) && isset($value['value']) && !empty($value['value'])) {
@ -1128,10 +1154,10 @@ if ($showQuestionClock) {
if (!in_array($origin, ['learnpath', 'embeddable'])) {
echo '<div id="highlight-plugin" class="glossary-content">';
}
if ($reminder == 2) {
if (2 === $reminder) {
$data_tracking = $exercise_stat_info['data_tracking'];
$data_tracking = explode(',', $data_tracking);
$current_question = 1; //set by default the 1st question
$current_question = 1; // Set by default the 1st question
if (!empty($myRemindList)) {
// Checking which questions we are going to call from the remind list
@ -1221,6 +1247,24 @@ if (!empty($questionList)) {
}
}
if ($allowBlockCategory &&
ONE_PER_PAGE == $objExercise->type &&
EX_Q_SELECTION_CATEGORIES_ORDERED_QUESTIONS_RANDOM == $selectionType
) {
if (0 === $isLastQuestionInCategory && 2 === $reminder) {
$endReminderValue = false;
if (!empty($myRemindList)) {
$endValue = end($myRemindList);
if ($endValue == $questionId) {
$endReminderValue = true;
}
}
if ($endReminderValue) {
$isLastQuestionInCategory = $categoryId;
}
}
}
$saveIcon = Display::return_icon(
'save.png',
get_lang('Saved'),
@ -1229,6 +1273,7 @@ $saveIcon = Display::return_icon(
false,
true
);
$loading = Display::returnFontAwesomeIcon('spinner', null, true, 'fa-spin');
echo '<script>
function addExerciseEvent(elm, evType, fn, useCapture) {
@ -1280,25 +1325,11 @@ echo '<script>
}
});
$(".main_question").mouseover(function() {
//$(this).find(".exercise_save_now_button").show();
//$(this).addClass("question_highlight");
});
$(".main_question").mouseout(function() {
//$(this).find(".exercise_save_now_button").hide();
$(this).removeClass("question_highlight");
});
$(".no_remind_highlight").hide();
// if the users validates the form using return key,
// prevent form action and simulates click on validation button
/*$("#exercise_form").submit(function(){
$(".question-validate-btn").first().trigger("click");
return false;
});*/
$("form#exercise_form").prepend($("#exercise-description"));
$(\'button[name="previous_question_and_save"]\').on("touchstart click", function (e) {
@ -1321,10 +1352,52 @@ echo '<script>
save_question_list(questionList);
});
$(\'button[name="save_now"]\').on(\'touchstart click\', function (e) {
$(\'button[name="check_answers"]\').on(\'touchstart click\', function (e) {
e.preventDefault();
e.stopPropagation();
var $this = $(this);
var urlExtra = $this.data(\'url\') || null;
var questionId = parseInt($this.data(\'question\')) || 0;
save_now(questionId, "check_answers");
var checkUrl = "'.$checkAnswersUrl.'";
$("#global-modal").attr("data-keyboard", "false");
$("#global-modal").attr("data-backdrop", "static");
$("#global-modal").find(".close").hide();
$("#global-modal .modal-body").load(checkUrl, function() {
$("#global-modal .modal-body").append("<div class=\"btn-group\"></div>");
var continueTest = $("<a>",{
text: "'.addslashes(get_lang('ContinueTest')).'",
title: "'.addslashes(get_lang('ContinueTest')).'",
href: "javascript:void(0);",
click: function(){
$(this).attr("disabled", "disabled");
$("#global-modal").modal("hide");
$("#global-modal .modal-body").html("");
}
}).addClass("btn btn-default").appendTo("#global-modal .modal-body .btn-group");
$("<a>",{
text: "'.addslashes(get_lang('EndTest')).'",
title: "'.addslashes(get_lang('EndTest')).'",
href: "javascript:void(0);",
click: function() {
$(this).attr("disabled", "disabled");
continueTest.attr("disabled", "disabled");
save_now(questionId, urlExtra);
$("#global-modal .modal-body").html("<span style=\"text-align:center\">'.addslashes($loading).addslashes(get_lang('Loading')).'</span>");
}
}).addClass("btn btn-primary").appendTo("#global-modal .modal-body .btn-group");
});
$("#global-modal").modal("show");
});
$(\'button[name="save_now"]\').on(\'touchstart click\', function (e) {
e.preventDefault();
e.stopPropagation();
var
$this = $(this),
questionId = parseInt($this.data(\'question\')) || 0,
@ -1413,8 +1486,7 @@ echo '<script>
dataparam += remind_list ? ("&" + remind_list) : "";
dataparam += my_choiceDc ? ("&" + my_choiceDc) : "";
$("#save_for_now_"+question_id).html(\''.
Display::returnFontAwesomeIcon('spinner', null, true, 'fa-spin').'\');
$("#save_for_now_"+question_id).html(\''.$loading.'\');
$.ajax({
type:"post",
url: "'.api_get_path(WEB_AJAX_PATH).'exercise.ajax.php?'.api_get_cidreq().'&a=save_exercise_by_now",
@ -1440,8 +1512,7 @@ echo '<script>
// If last question in category send to exercise_question_reminder.php
if ('.$isLastQuestionInCategory.' > 0 ) {
url = "exercise_question_reminder.php?'.$params.'&num='.$current_question.
'&category_id='.$isLastQuestionInCategory.'";
url = "exercise_question_reminder.php?'.$params.'&num='.($current_question - 1).'&category_id='.$isLastQuestionInCategory.'";
}
if (url_extra) {
@ -1450,6 +1521,10 @@ echo '<script>
$("#save_for_now_"+question_id).html(\''.
Display::return_icon('save.png', get_lang('Saved'), [], ICON_SIZE_SMALL).'\');
if ("check_answers" === url_extra) {
return true;
}
// window.quizTimeEnding will be reset in exercise.class.php
if (window.quizTimeEnding) {
redirectExerciseToResult();
@ -1489,7 +1564,7 @@ echo '<script>
});
free_answers = $.param(free_answers);
$("#save_all_response").html(\''.Display::returnFontAwesomeIcon('spinner', null, true, 'fa-spin').'\');
$("#save_all_response").html(\''.$loading.'\');
var requestData = "'.$params.'&type=all";
requestData += "&" + my_choice;

@ -338,6 +338,10 @@ function aiken_parse_file(&$exercise_info, $exercisePath, $file, $questionFile)
continue;
}
if (false !== strpos($data[$next], 'DESCRIPTION:')) {
continue;
}
// Check if next has score, otherwise loop too next question.
if (false === strpos($data[$next], 'SCORE:')) {
$answers_array = [];
@ -351,11 +355,21 @@ function aiken_parse_file(&$exercise_info, $exercisePath, $file, $questionFile)
continue;
} elseif (preg_match('/^DESCRIPTION:\s?(.*)/', $info, $matches)) {
$exercise_info['question'][$question_index]['description'] = $matches[1];
$next = $line + 1;
if (false !== strpos($data[$next], 'ANSWER_EXPLANATION:')) {
continue;
}
// Check if next has score, otherwise loop too next question.
if (false === strpos($data[$next], 'SCORE:')) {
$answers_array = [];
$question_index++;
continue;
}
} elseif (preg_match('/^ANSWER_EXPLANATION:\s?(.*)/', $info, $matches)) {
// Comment of correct answer
$correct_answer_index = array_search($matches[1], $answers_array);
$exercise_info['question'][$question_index]['feedback'] = $matches[1];
$next = $line + 1;
// Check if next has score, otherwise loop too next question.
if (false === strpos($data[$next], 'SCORE:')) {

@ -27,7 +27,7 @@ if (empty($id)) {
api_not_allowed($show_headers);
}
$is_allowedToEdit = api_is_allowed_to_edit(null, true) || $is_courseTutor;
$is_allowedToEdit = api_is_allowed_to_edit(null, true) || api_is_course_tutor();
// Getting results from the exe_id. This variable also contain all the information about the exercise
$track_exercise_info = ExerciseLib::get_exercise_track_exercise_info($id);

@ -30,23 +30,15 @@ if (!$result) {
$sessionId = api_get_session_id();
$courseCode = api_get_course_id();
$courseId = api_get_course_int_id();
if (empty($sessionId)) {
$students = CourseManager:: get_student_list_from_course_code(
$courseCode,
false
);
$students = CourseManager::get_student_list_from_course_code($courseCode, false);
} else {
$students = CourseManager:: get_student_list_from_course_code(
$courseCode,
true,
$sessionId
);
$students = CourseManager::get_student_list_from_course_code($courseCode, true, $sessionId);
}
$count_students = count($students);
//$question_list = $objExercise->get_validated_question_list();
$totalQuestions = $objExercise->getQuestionCount(); //Get total of questions
$question_list = $objExercise->getQuestionForTeacher(0, $totalQuestions); // get questions from 0 to total
$questionList = $objExercise->getQuestionForTeacher(0, $objExercise->getQuestionCount());
$data = [];
// Question title # of students who tool it Lowest score Average Highest score Maximum score
@ -60,14 +52,13 @@ $headers = [
get_lang('Weighting'),
];
if (!empty($question_list)) {
foreach ($question_list as $question_id) {
if (!empty($questionList)) {
foreach ($questionList as $question_id) {
$questionObj = Question::read($question_id);
$exercise_stats = ExerciseLib::get_student_stats_by_question(
$exerciseStats = ExerciseLib::get_student_stats_by_question(
$question_id,
$exerciseId,
$courseCode,
$courseId,
$sessionId,
true
);
@ -92,14 +83,13 @@ if (!empty($question_list)) {
false,
$count_users.' / '.$count_students
);
$data[$question_id]['lowest_score'] = round($exercise_stats['min'], 2);
$data[$question_id]['average_score'] = round($exercise_stats['average'], 2);
$data[$question_id]['highest_score'] = round($exercise_stats['max'], 2);
$data[$question_id]['lowest_score'] = round($exerciseStats['min'], 2);
$data[$question_id]['average_score'] = round($exerciseStats['average'], 2);
$data[$question_id]['highest_score'] = round($exerciseStats['max'], 2);
$data[$question_id]['max_score'] = round($questionObj->weighting, 2);
}
}
// Format A table
$table = new HTML_Table(['class' => 'table table-hover table-striped data_table']);
$row = 0;
$column = 0;
@ -130,14 +120,14 @@ $headers = [
$data = [];
if (!empty($question_list)) {
if (!empty($questionList)) {
$id = 0;
foreach ($question_list as $question_id) {
foreach ($questionList as $question_id) {
$questionObj = Question::read($question_id);
$exercise_stats = ExerciseLib::get_student_stats_by_question(
$exerciseStats = ExerciseLib::get_student_stats_by_question(
$question_id,
$exerciseId,
$courseCode,
$courseId,
$sessionId,
true
);
@ -212,7 +202,7 @@ if (!empty($question_list)) {
$answer_id,
$question_id,
$exerciseId,
$courseCode,
$courseId,
$sessionId,
MATCHING
);
@ -266,7 +256,7 @@ if (!empty($question_list)) {
$real_answer_id,
$question_id,
$exerciseId,
$courseCode,
$courseId,
$sessionId
);
$percentage = 0;

@ -129,14 +129,11 @@ if ($searchSessionAndCourse || $searchCourseOnly) {
}
if (!is_null($gradebook)) {
$exportAllLink = api_get_path(WEB_CODE_PATH)."gradebook/gradebook_display_certificate.php?";
$exportAllLink .= http_build_query([
"action" => "export_all_certificates",
"cidReq" => $selectedCourseInfo['code'],
"id_session" => 0,
"gidReq" => 0,
"cat_id" => $gradebook->get_id(),
]);
$exportAllLink = GradebookUtils::returnJsExportAllCertificates(
'#btn-export-all',
$gradebook->get_id(),
$selectedCourseInfo['code']
);
$sessionName = api_get_session_name($selectedSession);
$courseName = api_get_course_info($selectedCourseInfo['code'])['title'];

@ -0,0 +1,31 @@
<?php
/* For licensing terms, see /license.txt */
/**
* Command to export certificates for a gradebook categori in course (or course session).
*
* <code>php main/gradebook/cli/export_all_certificated.php COURSE_CODE SESSION_ID CATEGORY_ID [USER_IDS]</code>
*/
require_once __DIR__.'/../../inc/global.inc.php';
if (PHP_SAPI !== 'cli') {
exit(get_lang('NotAllowed'));
}
$courseCode = $argv[1];
$sessionId = $argv[2];
$categoryId = $argv[3];
$userList = isset($argv[4]) ? explode(',', $argv[4]) : [];
$date = api_get_utc_datetime(null, false, true);
$pdfName = 'certs_'.$courseCode.'_'.$sessionId.'_'.$categoryId.'_'.$date->format('Y-m-d');
$finalFile = api_get_path(SYS_ARCHIVE_PATH)."$pdfName.pdf";
if (file_exists($finalFile)) {
unlink(api_get_path(SYS_ARCHIVE_PATH)."$pdfName.pdf");
}
Category::exportAllCertificates($categoryId, $userList, $courseCode, true, $pdfName);

@ -125,31 +125,6 @@ switch ($action) {
);
$content = $form->returnForm();
break;
case 'export_all_certificates':
if ($allowCustomCertificate) {
$params = 'course_code='.api_get_course_id().
'&session_id='.api_get_session_id().
'&'.api_get_cidreq().
'&cat_id='.$categoryId;
$url = api_get_path(WEB_PLUGIN_PATH).
'customcertificate/src/print_certificate.php?export_all_in_one=1&'.$params;
} else {
if (api_is_student_boss()) {
$userGroup = new UserGroup();
$userList = $userGroup->getGroupUsersByUser(api_get_user_id());
} else {
$userList = [];
if (!empty($filterOfficialCodeGet)) {
$userList = UserManager::getUsersByOfficialCode($filterOfficialCodeGet);
}
}
Category::exportAllCertificates($categoryId, $userList);
}
header('Location: '.$url);
exit;
break;
case 'export_all_certificates_zip':
if ($allowCustomCertificate) {
$params = 'course_code='.api_get_course_id().
@ -280,10 +255,27 @@ $actions .= Display::url(
$hideCertificateExport = api_get_setting('hide_certificate_export_link');
if (count($certificate_list) > 0 && $hideCertificateExport !== 'true') {
if ($allowCustomCertificate) {
$actions = Display::url(
Display::return_icon('pdf.png', get_lang('ExportAllCertificatesToPDF'), [], ICON_SIZE_MEDIUM),
api_get_path(WEB_PLUGIN_PATH)
.'customcertificate/src/print_certificate.php?'.api_get_cidreq().'&'
.http_build_query(
[
'export_all_in_one' => 1,
'course_code' => api_get_course_id(),
'session_id' => api_get_session_id(),
'cat_id' => $categoryId,
]
)
);
} else {
$actions .= Display::url(
Display::return_icon('pdf.png', get_lang('ExportAllCertificatesToPDF'), [], ICON_SIZE_MEDIUM),
$url.'&action=export_all_certificates'
'#',
['id' => 'btn-export-all']
);
}
if ($allowCustomCertificate) {
$actions .= Display::url(
@ -350,5 +342,13 @@ if (count($certificate_list) == 0) {
}
echo '</tbody>';
echo '</table>';
echo GradebookUtils::returnJsExportAllCertificates(
'#btn-export-all',
$categoryId,
api_get_course_id(),
api_get_session_id(),
$filterOfficialCodeGet
);
}
Display::display_footer();

@ -211,29 +211,31 @@ if (!empty($_GET['export_report']) &&
) {
if (api_is_platform_admin() || api_is_course_admin() || api_is_session_general_coach() || $isDrhOfCourse) {
$user_id = null;
if (empty($_SESSION['export_user_fields'])) {
$_SESSION['export_user_fields'] = false;
}
if (!api_is_allowed_to_edit(false, false) && !api_is_course_tutor()) {
if (!api_is_allowed_to_edit() && !api_is_course_tutor()) {
$user_id = api_get_user_id();
}
$params['show_official_code'] = true;
$printable_data = GradebookUtils::get_printable_data(
$onlyScore = isset($_GET['only_score']) && 1 === (int) $_GET['only_score'];
$printableData = GradebookUtils::get_printable_data(
$cat[0],
$users,
$alleval,
$alllinks,
$params,
$mainCourseCategory[0]
$mainCourseCategory[0],
$onlyScore
);
switch ($_GET['export_format']) {
case 'xls':
ob_start();
$export = new GradeBookResult();
$export->exportCompleteReportXLS($printable_data);
$export->exportCompleteReportXLS($printableData);
$content = ob_get_contents();
ob_end_clean();
echo $content;
@ -241,7 +243,7 @@ if (!empty($_GET['export_report']) &&
case 'doc':
ob_start();
$export = new GradeBookResult();
$export->exportCompleteReportDOC($printable_data);
$export->exportCompleteReportDOC($printableData);
$content = ob_get_contents();
ob_end_clean();
echo $content;
@ -250,7 +252,7 @@ if (!empty($_GET['export_report']) &&
default:
ob_start();
$export = new GradeBookResult();
$export->exportCompleteReportCSV($printable_data);
$export->exportCompleteReportCSV($printableData);
$content = ob_get_contents();
ob_end_clean();
echo $content;
@ -263,6 +265,37 @@ if (!empty($_GET['export_report']) &&
}
$this_section = SECTION_COURSES;
if (isset($_GET['selectcat']) && ($_SESSION['studentview'] === 'teacherview')) {
$htmlHeadXtra[] = '<script>
$(function() {
$("#dialog:ui-dialog").dialog("destroy");
$("#dialog-confirm").dialog({
autoOpen: false,
show: "blind",
resizable: false,
height:300,
modal: true
});
$(".export_opener").click(function() {
var targetUrl = $(this).attr("href");
$("#dialog-confirm").dialog({
width:400,
height:300,
buttons: {
"'.addslashes(get_lang('Download')).'": function() {
let onlyScore = $("input[name=only_score]").prop("checked") ? 1 : 0;
location.href = targetUrl+"&only_score="+onlyScore;
$(this).dialog("close");
}
}
});
$("#dialog-confirm").dialog("open");
return false;
});
});
</script>';
}
if (isset($_GET['exportpdf'])) {
$export_pdf_form->display();

@ -906,7 +906,6 @@ if (isset($first_time) && $first_time == 1 && api_is_allowed_to_edit(null, true)
foreach ($components as $component) {
$gradebook = new Gradebook();
$params = [];
$params['name'] = $component['acronym'];
$params['description'] = $component['title'];
$params['user_id'] = api_get_user_id();
@ -918,8 +917,9 @@ if (isset($first_time) && $first_time == 1 && api_is_allowed_to_edit(null, true)
$gradebook->save($params);
}
// Reloading cats
$cats = Category:: load(
$cats = Category::load(
null,
null,
$course_code,
@ -1052,9 +1052,7 @@ if (isset($first_time) && $first_time == 1 && api_is_allowed_to_edit(null, true)
}
api_set_in_gradebook();
$contents = ob_get_contents();
ob_end_clean();
$view = new Template($viewTitle);

@ -604,6 +604,7 @@ class GradebookUtils
* @param $alllinks
* @param $params
* @param null $mainCourseCategory
* @param bool $onlyScore
*
* @return array
*/
@ -613,7 +614,8 @@ class GradebookUtils
$alleval,
$alllinks,
$params,
$mainCourseCategory = null
$mainCourseCategory = null,
$onlyScore = false
) {
$datagen = new FlatViewDataGenerator(
$users,
@ -624,29 +626,28 @@ class GradebookUtils
);
$offset = isset($_GET['offset']) ? (int) $_GET['offset'] : 0;
// step 2: generate rows: students
$datagen->category = $cat;
$count = (($offset + 10) > $datagen->get_total_items_count()) ? ($datagen->get_total_items_count() - $offset) : GRADEBOOK_ITEM_LIMIT;
$header_names = $datagen->get_header_names($offset, $count, true);
$data_array = $datagen->get_data(
$totalItems = $datagen->get_total_items_count();
$count = (($offset + 10) > $totalItems) ? ($totalItems - $offset) : GRADEBOOK_ITEM_LIMIT;
$headers = $datagen->get_header_names($offset, $count, true);
$list = $datagen->get_data(
FlatViewDataGenerator::FVDG_SORT_LASTNAME,
0,
null,
$offset,
$count,
$onlyScore,
true,
true
$onlyScore
);
$result = [];
foreach ($data_array as $data) {
foreach ($list as $data) {
$result[] = array_slice($data, 1);
}
$return = [$header_names, $result];
return $return;
return [$headers, $result];
}
/**
@ -1704,4 +1705,64 @@ class GradebookUtils
Database::update($table, $params, ['id = ?' => $commentInfo['id']]);
}
}
public static function returnJsExportAllCertificates(
$buttonSelector,
$categoryId,
$courseCode,
$sessionId = 0,
$filterOfficialCodeGet = null
) {
$params = [
'a' => 'export_all_certificates',
'cat_id' => $categoryId,
'cidReq' => $courseCode,
'id_session' => $sessionId,
'filter' => $filterOfficialCodeGet,
];
$urlExportAll = 'gradebook.ajax.php?'.http_build_query($params);
$params['a'] = 'verify_export_all_certificates';
$urlVerifyExportAll = 'gradebook.ajax.php?'.http_build_query($params);
$imgSrcLoading = api_get_path(WEB_LIBRARY_JS_PATH).'loading.gif';
$imgSrcPdf = Display::return_icon('pdf.png', '', [], ICON_SIZE_MEDIUM, false, true);
return "<script>
$(function () {
var \$btnExport = $('$buttonSelector'),
interval = 0;
function verifyExportSuccess (response) {
if (response.length > 0) {
\$btnExport.find('img').prop('src', '$imgSrcPdf');
window.clearInterval(interval);
window.removeEventListener('beforeunload', onbeforeunloadListener);
window.location.href = response;
}
}
function exportAllSuccess () {
interval = window.setInterval(
function () {
$.ajax(_p.web_ajax + '$urlVerifyExportAll').then(verifyExportSuccess);
},
15000
);
}
function onbeforeunloadListener (e) {
e.preventDefault();
e.returnValue = '';
}
\$btnExport.on('click', function (e) {
e.preventDefault();
\$btnExport.find('img').prop({src: '$imgSrcLoading', width: 40, height: 40});
window.addEventListener('beforeunload', onbeforeunloadListener);
$.ajax(_p.web_ajax + '$urlExportAll').then(exportAllSuccess);
});
});
</script>";
}
}

@ -2267,9 +2267,19 @@ class Category implements GradebookItem
/**
* @param int $catId
* @param array $userList
*/
public static function exportAllCertificates($catId, $userList = [])
{
* @param string|null $courseCode
* @param bool $generateToFile
* @param string $pdfName
*
* @throws \MpdfException
*/
public static function exportAllCertificates(
$catId,
$userList = [],
$courseCode = null,
$generateToFile = false,
$pdfName = ''
) {
$orientation = api_get_configuration_value('certificate_pdf_orientation');
$params['orientation'] = 'landscape';
@ -2281,8 +2291,8 @@ class Category implements GradebookItem
$params['right'] = 0;
$params['top'] = 0;
$params['bottom'] = 0;
$page_format = $params['orientation'] === 'landscape' ? 'A4-L' : 'A4';
$pdf = new PDF($page_format, $params['orientation'], $params);
$pageFormat = $params['orientation'] === 'landscape' ? 'A4-L' : 'A4';
$pdf = new PDF($pageFormat, $params['orientation'], $params);
if (api_get_configuration_value('add_certificate_pdf_footer')) {
$pdf->setCertificateFooter();
}
@ -2310,10 +2320,13 @@ class Category implements GradebookItem
// stuff) and return as one multiple-pages PDF
$pdf->html_to_pdf(
$certificate_path_list,
get_lang('Certificates'),
null,
empty($pdfName) ? get_lang('Certificates') : $pdfName,
$courseCode,
false,
false
false,
true,
'',
$generateToFile
);
}
}

@ -162,6 +162,12 @@ class DisplayGradebook
'selectcat' => $catobj->get_id(),
]);
$scoreRanking = ScoreDisplay::instance()->get_custom_score_display_settings();
$attributes = [];
if (!empty($scoreRanking)) {
$attributes = ['class' => 'export_opener'];
}
$header .= Display::url(
Display::return_icon(
'export_csv.png',
@ -169,7 +175,8 @@ class DisplayGradebook
'',
ICON_SIZE_MEDIUM
),
$exportCsvUrl
$exportCsvUrl,
$attributes
);
$exportXlsUrl = api_get_self().'?'.api_get_cidreq().'&'.http_build_query([
@ -185,7 +192,8 @@ class DisplayGradebook
'',
ICON_SIZE_MEDIUM
),
$exportXlsUrl
$exportXlsUrl,
$attributes
);
$exportDocUrl = api_get_self().'?'.api_get_cidreq().'&'.http_build_query([
@ -201,7 +209,8 @@ class DisplayGradebook
'',
ICON_SIZE_MEDIUM
),
$exportDocUrl
$exportDocUrl,
$attributes
);
$exportPrintUrl = api_get_self().'?'.api_get_cidreq().'&'.http_build_query([
@ -239,7 +248,27 @@ class DisplayGradebook
);
$header .= '</div>';
echo $header;
$dialog = '';
if (!empty($scoreRanking)) {
$dialog = '<div id="dialog-confirm" style="display:none" title="'.get_lang('ConfirmYourChoice').'">';
$form = new FormValidator(
'report',
'post',
null,
null,
['class' => 'form-vertical']
);
$form->addCheckBox(
'only_score',
null,
get_lang('OnlyNumbers')
);
$dialog .= $form->returnForm();
$dialog .= '</div>';
}
echo $header.$dialog;
}
/**

@ -523,7 +523,7 @@ class EvalForm extends FormValidator
private function build_basic_form($edit = 0)
{
$form_title = get_lang('NewEvaluation');
if (!empty($_GET['editeval']) && $_GET['editeval'] == 1) {
if (!empty($_GET['editeval'])) {
$form_title = get_lang('EditEvaluation');
}

@ -348,16 +348,15 @@ class FlatViewTable extends SortableTable
$header = null;
if ($this->limit_enabled && $totalitems > GRADEBOOK_ITEM_LIMIT) {
$header .= '<table style="width: 100%; text-align: right; margin-left: auto; margin-right: auto;" border="0" cellpadding="2">'
.'<tbody>'
.'<tr>';
$header .= '<table
style="width: 100%; text-align: right; margin-left: auto; margin-right: auto;"
border="0" cellpadding="2"><tbody>
<tr>';
// previous X
$header .= '<td style="width:100%;">';
if ($this->offset >= GRADEBOOK_ITEM_LIMIT) {
$header .= '<a href="'.api_get_self()
.'?selectcat='.Security::remove_XSS($_GET['selectcat'])
.'&offset='.(($this->offset) - GRADEBOOK_ITEM_LIMIT)
$header .= '<a
href="'.api_get_self().'?selectcat='.(int) $_GET['selectcat'].'&offset='.(($this->offset) - GRADEBOOK_ITEM_LIMIT)
.(isset($_GET['search']) ? '&search='.Security::remove_XSS($_GET['search']) : '').'">'
.Display::return_icon(
'action_prev.png',
@ -434,10 +433,8 @@ class FlatViewTable extends SortableTable
$firstHeader = [];
while ($column < count($header_names)) {
$headerData = $header_names[$column];
if (is_array($headerData)) {
$countItems = count($headerData['items']);
$this->set_header(
$column,
$headerData['header'],
@ -445,9 +442,13 @@ class FlatViewTable extends SortableTable
'colspan="'.$countItems.'"'
);
if (count($headerData['items']) > 0) {
foreach ($headerData['items'] as $item) {
$firstHeader[] = '<span class="text-center">'.$item.'</span>';
}
} else {
$firstHeader[] = '&mdash;';
}
} else {
$this->set_header($column, $headerData, false, $thAttributes);
}
@ -509,6 +510,8 @@ class FlatViewTable extends SortableTable
*/
private function build_name_link($userId, $name)
{
return '<a href="user_stats.php?userid='.$userId.'&selectcat='.$this->selectcat->get_id().'&'.api_get_cidreq().'">'.$name.'</a>';
return '<a
href="user_stats.php?userid='.$userId.'&selectcat='.$this->selectcat->get_id().'&'.api_get_cidreq().'">'.
$name.'</a>';
}
}

@ -306,8 +306,9 @@ class FlatViewDataGenerator
$users_count = null,
$items_start = 0,
$items_count = null,
$ignore_score_color = false,
$show_all = false
$ignoreScoreColor = false,
$show_all = false,
$onlyScore = false
) {
// Do some checks on users/items counts, redefine if invalid values
if (!isset($users_count)) {
@ -371,7 +372,6 @@ class FlatViewDataGenerator
}
$parent_id = $this->category->get_parent_id();
if (0 == $parent_id ||
(isset($this->params['only_subcat']) && $this->params['only_subcat'] == $this->category->get_id())
) {
@ -440,12 +440,9 @@ class FlatViewDataGenerator
}
$row[] = $user[1];
$this->addExtraFieldColumnsData($row, $user[0]);
$item_value_total = 0;
$item_total = 0;
$allcat = $this->category->get_subcategories(
null,
$course_code,
@ -461,11 +458,14 @@ class FlatViewDataGenerator
$defaultStyle = (int) $style;
}
if ($onlyScore) {
$defaultStyle = SCORE_PERCENT;
}
if (0 == $parent_id && !empty($allcat)) {
/** @var Category $sub_cat */
foreach ($allcat as $sub_cat) {
$score = $sub_cat->calc_score($user_id);
if ('true' === $detailAdminView) {
$links = $sub_cat->get_links();
/** @var ExerciseLink $link */
@ -474,7 +474,9 @@ class FlatViewDataGenerator
$linkScore = $link->calc_score($user_id);
$linkScoreList[] = $scoreDisplay->display_score(
$linkScore,
SCORE_SIMPLE
$defaultStyle,
null,
$ignoreScoreColor
);
}
@ -485,7 +487,9 @@ class FlatViewDataGenerator
$evalScore = $evaluation->calc_score($user_id);
$evalScoreList[] = $scoreDisplay->display_score(
$evalScore,
SCORE_SIMPLE
$defaultStyle,
null,
$ignoreScoreColor
);
}
}
@ -505,27 +509,34 @@ class FlatViewDataGenerator
if (!empty($style)) {
$defaultShowPercentageValue = $style;
}
if ($onlyScore) {
$defaultShowPercentageValue = SCORE_PERCENT;
}
$real_score = $scoreDisplay->display_score(
$real_score,
$defaultShowPercentageValue,
true
true,
$ignoreScoreColor
);
$temp_score = $scoreDisplay->display_score(
$score,
SCORE_DIV_SIMPLE_WITH_CUSTOM,
null
null,
$ignoreScoreColor
);
$temp_score = Display::tip($real_score, $temp_score);
} else {
$real_score = $scoreDisplay->display_score(
$real_score,
SCORE_DIV_PERCENT,
SCORE_ONLY_SCORE
SCORE_ONLY_SCORE,
$ignoreScoreColor
);
$temp_score = $scoreDisplay->display_score(
$score,
$defaultStyle,
null
null,
$ignoreScoreColor
);
$temp_score = Display::tip($temp_score, $real_score);
}
@ -563,7 +574,11 @@ class FlatViewDataGenerator
$items_count,
$items_start,
$show_all,
$row
$row,
null,
[],
$ignoreScoreColor,
$onlyScore
);
$item_value_total += $result['item_value_total'];
$evaluationsAdded = $result['evaluations_added'];
@ -579,19 +594,22 @@ class FlatViewDataGenerator
$show_all,
$row,
$mainCategoryId,
$evaluationsAdded
$evaluationsAdded,
$ignoreScoreColor,
$onlyScore
);
$item_total += $result['item_total'];
$item_value_total += $result['item_value_total'];
$total_score = [$item_value_total, $item_total];
$style = api_get_configuration_value('gradebook_report_score_style');
$customDisplayIsStandalone = api_get_configuration_value('gradebook_score_display_custom_standalone')
&& $scoreDisplay->is_custom();
$customDisplayIsStandalone =
api_get_configuration_value('gradebook_score_display_custom_standalone') &&
$scoreDisplay->is_custom();
if (!$show_all) {
$defaultStyle = empty($style) ? SCORE_DIV_PERCENT : (int) $style;
$displayScore = $scoreDisplay->display_score($total_score, $defaultStyle);
//$defaultStyle = empty($style) ? SCORE_DIV_PERCENT : (int) $style;
$displayScore = $scoreDisplay->display_score($total_score, $defaultStyle, null, $ignoreScoreColor);
if (!empty($model)) {
$displayScore = ExerciseLib::show_score($total_score[0], $total_score[1]);
}
@ -601,8 +619,11 @@ class FlatViewDataGenerator
$row[] = $displayScore;
}
} else {
$defaultStyle = empty($style) ? SCORE_DIV_SIMPLE_WITH_CUSTOM_LETTERS : (int) $style;
$displayScore = $scoreDisplay->display_score($total_score, $defaultStyle);
/*$defaultStyle = empty($style) ? SCORE_DIV_SIMPLE_WITH_CUSTOM_LETTERS : (int) $style;
if ($ignoreScoreColor) {
$defaultStyle = SCORE_DIV_PERCENT;
}*/
$displayScore = $scoreDisplay->display_score($total_score, $defaultStyle, null, $ignoreScoreColor);
if (!empty($model)) {
$displayScore = ExerciseLib::show_score($total_score[0], $total_score[1]);
}
@ -648,7 +669,9 @@ class FlatViewDataGenerator
$show_all,
&$row,
$parentCategoryIdFilter = null,
$evaluationsAlreadyAdded = []
$evaluationsAlreadyAdded = [],
$ignoreScoreDecorations = false,
$onlyScore = false
) {
// Generate actual data array
$scoreDisplay = ScoreDisplay::instance();
@ -662,6 +685,10 @@ class FlatViewDataGenerator
if (!empty($style)) {
$defaultStyle = (int) $style;
}
if ($onlyScore) {
$defaultStyle = SCORE_PERCENT;
}
$showPercentage = api_get_setting('gradebook_show_percentage_in_reports');
for ($count = 0; $count < $items_count && ($items_start + $count < count($this->evals_links)); $count++) {
/** @var AbstractLink $item */
@ -681,13 +708,10 @@ class FlatViewDataGenerator
$evaluationsAdded[] = $item->get_id();
$score = $item->calc_score($user_id);
$real_score = $score;
$divide = isset($score[1]) && !empty($score[1]) && $score[1] > 0 ? $score[1] : 1;
// Sub cat weight
$item_value = isset($score[0]) ? $score[0] / $divide : null;
// Fixing total when using one or multiple gradebooks.
if (empty($parentCategoryIdFilter)) {
if (0 == $this->category->get_parent_id()) {
@ -708,11 +732,15 @@ class FlatViewDataGenerator
}
$item_total += $item->get_weight();
$totalType = SCORE_DIV_PERCENT;
if ($onlyScore) {
$totalType = SCORE_PERCENT;
}
$complete_score = $scoreDisplay->display_score(
$score,
SCORE_DIV_PERCENT,
SCORE_ONLY_SCORE
$totalType,
SCORE_ONLY_SCORE,
$ignoreScoreDecorations
);
if ('false' === $showPercentage) {
@ -720,19 +748,29 @@ class FlatViewDataGenerator
if (!empty($style)) {
$defaultShowPercentageValue = $style;
}
if ($onlyScore) {
$defaultShowPercentageValue = SCORE_PERCENT;
}
$real_score = $scoreDisplay->display_score(
$real_score,
$defaultShowPercentageValue
$defaultShowPercentageValue,
null,
$ignoreScoreDecorations
);
$temp_score = $scoreDisplay->display_score(
[$item_value, null],
SCORE_DIV_SIMPLE_WITH_CUSTOM
$defaultStyle,
null,
$ignoreScoreDecorations
);
$temp_score = Display::tip($real_score, $temp_score);
} else {
$temp_score = $scoreDisplay->display_score(
$real_score,
$defaultStyle
$defaultStyle,
null,
$ignoreScoreDecorations
);
$temp_score = Display::tip($temp_score, $complete_score);
}
@ -985,7 +1023,6 @@ class FlatViewDataGenerator
$item_total += $item->get_weight();
$score_denom = (0 == $score[1]) ? 1 : $score[1];
$score_final = ($score[0] / $score_denom) * 100;
$displayScore = trim(
$scoreDisplay->display_score(
$score,
@ -1002,7 +1039,10 @@ class FlatViewDataGenerator
];
}
$total_score = [$item_value, $item_total];
$score_final = 0;
if (!empty($item_total)) {
$score_final = ($item_value / $item_total) * 100;
}
if ($displayWarning) {
echo Display::return_message($total_score[1], 'warning');
}

@ -684,7 +684,7 @@ class GradebookDataGenerator
$score,
SCORE_DIV,
SCORE_BOTH,
false,
true,
true,
true
);
@ -774,8 +774,7 @@ class GradebookDataGenerator
false,
true
);
$type = $item->get_item_type();
/*$type = $item->get_item_type();
if ('L' === $type && 'ExerciseLink' === get_class($item)) {
$display = ExerciseLib::show_score(
$score[0],
@ -789,7 +788,7 @@ class GradebookDataGenerator
false,
true
);
}
}*/
} else {
$display = ExerciseLib::show_score(
$score[0],

@ -28,16 +28,16 @@ class GradeBookResult
public function exportCompleteReportCSV($dato)
{
$filename = 'gradebook_results_'.gmdate('YmdGis').'.csv';
$data = '';
//build the results
//titles
foreach ($dato[0] as $header_col) {
if (!empty($header_col)) {
if (is_array($header_col)) {
if (isset($header_col['header'])) {
$data .= str_replace("\r\n", ' ', api_html_entity_decode(strip_tags($header_col['header']))).';';
$data .= str_replace(
"\r\n",
' ',
api_html_entity_decode(strip_tags($header_col['header']))
).';';
}
} else {
$data .= str_replace("\r\n", ' ', api_html_entity_decode(strip_tags($header_col))).';';
@ -47,9 +47,7 @@ class GradeBookResult
$data .= "\r\n";
$cant_students = count($dato[1]);
for ($i = 0; $i < $cant_students; $i++) {
$column = 0;
foreach ($dato[1][$i] as $col_name) {
$data .= str_replace("\r\n", ' ', api_html_entity_decode(strip_tags($col_name))).';';
}
@ -93,23 +91,24 @@ class GradeBookResult
$spreadsheet = new PHPExcel();
$spreadsheet->setActiveSheetIndex(0);
$worksheet = $spreadsheet->getActiveSheet();
$line = 1;
$column = 0;
//headers
foreach ($data[0] as $header_col) {
// headers.
foreach ($data[0] as $headerData) {
$title = $headerData;
if (isset($headerData['header'])) {
$title = $headerData['header'];
}
$title = html_entity_decode(strip_tags($title));
$worksheet->SetCellValueByColumnAndRow(
$column,
$line,
html_entity_decode(strip_tags($header_col))
$title
);
$column++;
}
$line++;
$cant_students = count($data[1]);
for ($i = 0; $i < $cant_students; $i++) {
$column = 0;
foreach ($data[1][$i] as $col_name) {
@ -147,12 +146,16 @@ class GradeBookResult
$table->addRow();
for ($i = 0; $i < count($data[0]); $i++) {
$table->addCell(1750)->addText(strip_tags($data[0][$i]));
$title = $data[0][$i];
if (isset($data[0][$i]['header'])) {
$title = $data[0][$i]['header'];
}
$title = strip_tags($title);
$table->addCell(1750)->addText($title);
}
foreach ($data[1] as $dataLine) {
$table->addRow();
for ($i = 0; $i < count($dataLine); $i++) {
$table->addCell(1750)->addText(strip_tags($dataLine[$i]));
}

@ -336,6 +336,11 @@ class ScoreDisplay
$ignoreDecimals = false,
$removeEmptyDecimals = false
) {
// No score available.
if (!is_array($score)) {
return '-';
}
$my_score = $score == 0 ? [] : $score;
switch ($type) {
case SCORE_BAR:
@ -348,22 +353,16 @@ class ScoreDisplay
return round($percentage);
break;
case SCORE_SIMPLE:
if (!isset($my_score[0])) {
$my_score[0] = 0;
}
return $this->format_score($my_score[0], $ignoreDecimals);
break;
}
if ($this->custom_enabled && isset($this->custom_display_conv)) {
$display = $this->displayDefault($my_score, $type, $ignoreDecimals, $removeEmptyDecimals);
if (false === $disableColor && $this->custom_enabled && isset($this->custom_display_conv)) {
$display = $this->displayDefault($my_score, $type, $ignoreDecimals, $removeEmptyDecimals, true);
} else {
// if no custom display set, use default display
$display = $this->displayDefault($my_score, $type, $ignoreDecimals, $removeEmptyDecimals);
}
if ($this->coloring_enabled && $disableColor == false) {
if ($this->coloring_enabled && false === $disableColor) {
$denom = isset($score[1]) && !empty($score[1]) && $score[1] > 0 ? $score[1] : 1;
$scoreCleaned = isset($score[0]) ? $score[0] : 0;
if (($scoreCleaned / $denom) < ($this->color_split_value / 100)) {
@ -440,53 +439,45 @@ class ScoreDisplay
* @param int $type
* @param bool $ignoreDecimals
* @param bool $removeEmptyDecimals
* @param bool $addScoreLabel
*
* @return string
*/
private function displayDefault($score, $type, $ignoreDecimals = false, $removeEmptyDecimals = false)
private function displayDefault($score, $type, $ignoreDecimals = false, $removeEmptyDecimals = false, $addScoreLabel = false)
{
$scoreLabel = '';
if ($addScoreLabel) {
$scoreLabel = $this->display_custom($score);
if (!empty($scoreLabel)) {
$scoreLabel = ' - '.$scoreLabel;
}
}
switch ($type) {
case SCORE_DIV: // X / Y
return $this->display_as_div($score, $ignoreDecimals, $removeEmptyDecimals);
return $this->display_as_div($score, $ignoreDecimals, $removeEmptyDecimals).$scoreLabel;
case SCORE_PERCENT: // XX %
return $this->display_as_percent($score);
return $this->display_as_percent($score).$scoreLabel;
case SCORE_DIV_PERCENT: // X / Y (XX %)
//return $this->display_as_div($score).' ('.$this->display_as_percent($score).')';
// 2020-10 Changed to XX % (X / Y)
return $this->display_as_percent($score).' ('.$this->display_as_div($score).')';
return $this->display_as_percent($score).' ('.$this->display_as_div($score).')'.$scoreLabel;
case SCORE_AVERAGE: // XX %
return $this->display_as_percent($score);
return $this->display_as_percent($score).$scoreLabel;
case SCORE_DECIMAL: // 0.50 (X/Y)
return $this->display_as_decimal($score);
return $this->display_as_decimal($score).$scoreLabel;
case SCORE_DIV_PERCENT_WITH_CUSTOM: // X / Y (XX %) - Good!
$custom = $this->display_custom($score);
if (!empty($custom)) {
$custom = ' - '.$custom;
}
$div = $this->display_as_div($score, false, $removeEmptyDecimals);
/*return
$div.
' ('.$this->display_as_percent($score).')'.$custom;*/
return
$this->display_as_percent($score).
' ('.$div.')'.$custom;
' ('.$div.')'.$scoreLabel;
case SCORE_DIV_SIMPLE_WITH_CUSTOM: // X - Good!
$custom = $this->display_custom($score);
if (!empty($custom)) {
$custom = ' - '.$custom;
}
return $this->display_simple_score($score).$custom;
return $this->display_simple_score($score).$scoreLabel;
break;
case SCORE_DIV_SIMPLE_WITH_CUSTOM_LETTERS:
$custom = $this->display_custom($score);
if (!empty($custom)) {
$custom = ' - '.$custom;
}
$score = $this->display_simple_score($score);
//needs sudo apt-get install php5-intl
// Needs sudo apt-get install php5-intl
if (class_exists('NumberFormatter')) {
$iso = api_get_language_isocode();
$f = new NumberFormatter($iso, NumberFormatter::SPELLOUT);
@ -495,10 +486,15 @@ class ScoreDisplay
$letters = " ($letters) ";
}
return $score.$letters.$custom;
break;
return $score.$letters.$scoreLabel;
case SCORE_CUSTOM: // Good!
return $this->display_custom($score);
case SCORE_SIMPLE:
if (!isset($score[0])) {
$score[0] = 0;
}
return $this->format_score($score[0], $ignoreDecimals).$scoreLabel;
}
}

@ -219,16 +219,11 @@ if (api_get_setting('allow_group_categories') === 'true') {
'title' => get_lang('DefaultGroupCategory'),
];
$group_cats = array_merge([$defaultCategory], $group_cats);
foreach ($group_cats as $index => $category) {
$categoryId = $category['id'];
$group_list = GroupManager::get_group_list($categoryId);
$groupToShow = GroupManager::process_groups($group_list, $categoryId);
if (empty($groupToShow)) {
continue;
}
if (empty($categoryId) && empty($group_list)) {
continue;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

@ -27,7 +27,8 @@ switch ($action) {
case 'preview':
$allowToEdit = (
api_is_allowed_to_edit(false, true) ||
(api_get_course_setting('allow_user_edit_announcement') && !api_is_anonymous())
(api_get_course_setting('allow_user_edit_announcement') && !api_is_anonymous()) ||
($sessionId && api_is_coach() && api_get_configuration_value('allow_coach_to_edit_announcements'))
);
$drhHasAccessToSessionContent = api_drh_can_access_all_session_content();

@ -258,12 +258,18 @@ switch ($action) {
}
break;
case 'search_user_by_course':
if (api_is_platform_admin()) {
$user = Database::get_main_table(TABLE_MAIN_USER);
$session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
$sessionId = $_GET['session_id'];
$course = api_get_course_info_by_id($_GET['course_id']);
$isPlatformAdmin = api_is_platform_admin();
$userIsSubscribedInCourse = CourseManager::is_user_subscribed_in_course(
api_get_user_id(),
$course['code'],
!empty($sessionId),
$sessionId
);
if ($isPlatformAdmin || $userIsSubscribedInCourse) {
$json = [
'items' => [],
];

@ -22,18 +22,20 @@ switch ($action) {
if (api_is_allowed_to_edit(null, true)) {
$criteria = [
'cId' => $course_id,
'sessionId' => 0,
//'sessionId' => 0,
'iid' => (int) $_GET['id'],
];
/** @var CTool $tool */
$tool = $repository->findOneBy($criteria);
$visibility = $tool->getVisibility();
$visibility = 0;
if ($allowEditionInSession && !empty($sessionId)) {
$newLink = str_replace('id_session=0', 'id_session='.$sessionId, $tool->getLink());
$criteria = [
'cId' => $course_id,
'sessionId' => $sessionId,
'name' => $tool->getName(),
//'iid' => (int) $_GET['id'],
'link' => $newLink,
];
/** @var CTool $tool */
@ -45,10 +47,11 @@ switch ($action) {
} else {
// Creates new row in c_tool
$toolInSession = clone $tool;
$toolInSession->setLink($newLink);
$toolInSession->setIid(0);
$toolInSession->setId(0);
$toolInSession->setVisibility(0);
$toolInSession->setSessionId($session_id);
$toolInSession->setSessionId($sessionId);
$em->persist($toolInSession);
$em->flush();
// Update id with iid
@ -58,12 +61,14 @@ switch ($action) {
// $tool will be updated later
$tool = $toolInSession;
}
} else {
$visibility = $tool->getVisibility();
}
$toolImage = $tool->getImage();
$customIcon = $tool->getCustomIcon();
if (api_get_setting('homepage_view') != 'activity_big') {
if (api_get_setting('homepage_view') !== 'activity_big') {
$toolImage = Display::return_icon(
$toolImage,
null,

@ -160,7 +160,6 @@ switch ($action) {
}
// 1. Setting variables needed by jqgrid
$action = $_GET['a'];
$exercise_id = (int) $_GET['exercise_id'];
$page = (int) $_REQUEST['page']; //page
$limit = (int) $_REQUEST['rows']; //quantity of rows
@ -395,13 +394,46 @@ switch ($action) {
echo 1;
exit;
break;
case 'check_answers':
if (false === api_is_allowed_to_session_edit()) {
echo 'error';
exit;
}
/** @var Exercise $objExercise */
$objExercise = Session::read('objExercise');
$questionList = Session::read('questionList');
$exeId = Session::read('exe_id');
// If exercise or question is not set then exit.
if (empty($questionList) || empty($objExercise)) {
echo 'error';
exit;
}
$statInfo = $objExercise->get_stat_track_exercise_info_by_exe_id($exeId);
echo Display::page_subheader(get_lang('VerificationOfAnsweredQuestions'));
echo $objExercise->getReminderTable($questionList, $statInfo, true);
break;
case 'save_exercise_by_now':
$course_info = api_get_course_info_by_id($course_id);
$course_id = $course_info['real_id'];
// Use have permissions to edit exercises results now?
if (api_is_allowed_to_session_edit()) {
if (false === api_is_allowed_to_session_edit()) {
echo 'error';
if ($debug) {
error_log(
'Exercises attempt '.$exeId.': Failed saving question(s) in course/session '.
$course_id.'/'.$session_id.
': The user ('.api_get_user_id().') does not have the permission to access this session now'
);
}
exit;
}
// "all" or "simple" strings means that there's one or all questions exercise type
$type = isset($_REQUEST['type']) ? $_REQUEST['type'] : null;
@ -431,7 +463,6 @@ switch ($action) {
error_log("--------------------------------");
}
// Exercise information.
/** @var Exercise $objExercise */
$objExercise = Session::read('objExercise');
@ -563,7 +594,7 @@ switch ($action) {
}
// Getting free choice data.
if (in_array($objQuestionTmp->type, [FREE_ANSWER, ORAL_EXPRESSION]) && $type === 'all') {
if ('all' === $type && in_array($objQuestionTmp->type, [FREE_ANSWER, ORAL_EXPRESSION])) {
$my_choice = isset($_REQUEST['free_choice'][$my_question_id]) && !empty($_REQUEST['free_choice'][$my_question_id])
? $_REQUEST['free_choice'][$my_question_id]
: null;
@ -617,7 +648,7 @@ switch ($action) {
}
}
// Deleting old attempt
// Deleting old attempt.
if (isset($attemptList) && !empty($attemptList[$my_question_id])) {
if ($debug) {
error_log("delete_attempt exe_id : $exeId, my_question_id: $my_question_id");
@ -777,18 +808,6 @@ switch ($action) {
if ($debug) {
error_log('Finished questions loop in save_exercise_by_now');
}
} else {
if ($debug) {
error_log(
'Exercises attempt '.$exeId.': Failed saving question(s) in course/session '.
$course_id.'/'.$session_id.
': The user ('.
api_get_user_id().
') does not have the permission to access this session now');
}
echo 'error';
exit;
}
if ($type === 'all') {
if ($debug) {

@ -1,4 +1,5 @@
<?php
/* For licensing terms, see /license.txt */
use Chamilo\CoreBundle\Entity\Tag;
@ -151,14 +152,11 @@ switch ($action) {
dataType: "json",
data: "values="+save,
success: function(data) {
console.log(data);
}
});
alert("'.get_lang('Saved').'");
location.reload();
return false;
});

@ -55,6 +55,44 @@ switch ($action) {
$form->display();
}
break;*/
case 'export_all_certificates':
$categoryId = (int) $_GET['cat_id'];
$filterOfficialCodeGet = isset($_GET['filter']) ? Security::remove_XSS($_GET['filter']) : null;
if (api_is_student_boss()) {
$userGroup = new UserGroup();
$userList = $userGroup->getGroupUsersByUser(api_get_user_id());
} else {
$userList = [];
if (!empty($filterOfficialCodeGet)) {
$userList = UserManager::getUsersByOfficialCode($filterOfficialCodeGet);
}
}
$courseCode = api_get_course_id();
$sessionId = api_get_session_id();
$commandScript = api_get_path(SYS_CODE_PATH).'gradebook/cli/export_all_certificates.php';
$userList = implode(',', $userList);
shell_exec("php $commandScript $courseCode $sessionId $categoryId $userList > /dev/null &");
break;
case 'verify_export_all_certificates':
$categoryId = (int) $_GET['cat_id'];
$courseCode = isset($_GET['cidReq']) ? Security::remove_XSS($_GET['cidReq']) : api_get_course_id();
$sessionId = isset($_GET['id_session']) ? (int) $_GET['id_session'] : api_get_session_id();
$date = api_get_utc_datetime(null, false, true);
$pdfName = 'certs_'.$courseCode.'_'.$sessionId.'_'.$categoryId.'_'.$date->format('Y-m-d');
$sysFinalFile = api_get_path(SYS_ARCHIVE_PATH)."$pdfName.pdf";
$webFinalFile = api_get_path(WEB_ARCHIVE_PATH)."$pdfName.pdf";
if (file_exists($sysFinalFile)) {
echo $webFinalFile;
}
break;
default:
echo '';
break;

@ -53,6 +53,7 @@ if (!in_array(
'get_work_user_list',
'get_work_user_list_others',
'get_work_user_list_all',
'get_work_pending_list',
'get_timelines',
'get_user_skill_ranking',
'get_usergroups',
@ -557,6 +558,21 @@ switch ($action) {
$work_id = $_REQUEST['work_id'];
$count = get_count_work($work_id);
break;
case 'get_work_pending_list':
require_once api_get_path(SYS_CODE_PATH).'work/work.lib.php';
$courseId = $_REQUEST['course'] ?? 0;
$status = $_REQUEST['status'] ?? 0;
$count = getAllWork(
null,
null,
null,
null,
$whereCondition,
true,
$courseId,
$status
);
break;
case 'get_work_user_list_others':
require_once api_get_path(SYS_CODE_PATH).'work/work.lib.php';
$work_id = $_REQUEST['work_id'];
@ -642,10 +658,7 @@ switch ($action) {
$whereCondition = " AND $whereCondition";
}
$count = ExerciseLib::get_count_exam_results(
$exercise_id,
$whereCondition
);
$count = ExerciseLib::get_count_exam_results($exercise_id, $whereCondition);
break;
case 'get_exercise_results_report':
api_protect_admin_script();
@ -1371,6 +1384,38 @@ switch ($action) {
$whereCondition
);
break;
case 'get_work_pending_list':
api_block_anonymous_users();
if (false === api_is_teacher()) {
exit;
}
$plagiarismColumns = [];
if (api_get_configuration_value('allow_compilatio_tool')) {
$plagiarismColumns = ['compilatio'];
}
$columns = [
'course',
'work_name',
'fullname',
'title',
'qualification',
'sent_date',
'qualificator_id',
'correction',
];
$columns = array_merge($columns, $plagiarismColumns);
$columns[] = 'actions';
$result = getAllWork(
$start,
$limit,
$sidx,
$sord,
$whereCondition,
false,
$courseId,
$status
);
break;
case 'get_work_user_list_others':
$plagiarismColumns = [];
if (api_get_configuration_value('allow_compilatio_tool')) {
@ -1539,7 +1584,7 @@ switch ($action) {
if (!empty($categoryList)) {
foreach ($categoryList as $categoryInfo) {
$label = 'category_'.$categoryInfo['id'];
if ($operation == 'excel') {
if ($operation === 'excel') {
$columns[] = $label.'_score_percentage';
$columns[] = $label.'_only_score';
$columns[] = $label.'_total';
@ -2474,6 +2519,7 @@ $allowed_actions = [
'get_work_user_list',
'get_work_user_list_others',
'get_work_user_list_all',
'get_work_pending_list',
'get_timelines',
'get_grade_models',
'get_event_email_template',

@ -151,13 +151,10 @@ switch ($action) {
api_protect_course_script(true);
// User access same as upload.php
$is_allowed_to_edit = api_is_allowed_to_edit(null, true);
$itemId = isset($_GET['item_id']) ? intval($_GET['item_id']) : '';
$itemId = isset($_GET['item_id']) ? (int) $_GET['item_id'] : '';
$result = [];
if (!empty($_FILES) && !empty($itemId)) {
$file = $_FILES['file'];
$courseInfo = api_get_course_info();
$workInfo = get_work_data_by_id($itemId);
$workInfoParent = get_work_data_by_id($workInfo['parent_id']);
@ -166,9 +163,7 @@ switch ($action) {
echo 'false';
break;
}
$work_table = Database::get_course_table(
TABLE_STUDENT_PUBLICATION
);
$work_table = Database::get_course_table(TABLE_STUDENT_PUBLICATION);
if (isset($resultUpload['url']) && !empty($resultUpload['url'])) {
$title = isset($resultUpload['filename']) && !empty($resultUpload['filename']) ? $resultUpload['filename'] : get_lang('Untitled');

@ -1649,7 +1649,8 @@ class AnnouncementManager
if (api_is_allowed_to_edit(false, true) ||
($allowUserEditSetting && !api_is_anonymous()) ||
($allowDrhAccess && api_is_drh())
($allowDrhAccess && api_is_drh()) ||
($session_id && api_is_coach() && api_get_configuration_value('allow_coach_to_edit_announcements'))
) {
// A.1. you are a course admin with a USER filter
// => see only the messages of this specific user + the messages of the group (s)he is member of.
@ -1867,7 +1868,6 @@ class AnnouncementManager
$results = [];
$emailIcon = '<i class="fa fa-envelope-o" title="'.get_lang('AnnounceSentByEmail').'"></i>';
$attachmentIcon = '<i class="fa fa-paperclip" title="'.get_lang('Attachment').'"></i>';
$editIcon = Display::return_icon(
'edit.png',
get_lang('Edit'),
@ -1909,7 +1909,6 @@ class AnnouncementManager
if (!in_array($row['id'], $displayed)) {
$actionUrl = api_get_path(WEB_CODE_PATH).'announcements/announcements.php?'
.api_get_cidreq_params($courseInfo['code'], $session_id, $row['to_group_id']);
$sent_to_icon = '';
// the email icon
if ($row['email_sent'] == '1') {
@ -1957,10 +1956,8 @@ class AnnouncementManager
$attachment_icon = ' '.$attachmentIcon;
}
/* TITLE */
$user_info = api_get_user_info($row['insert_user_id']);
$username = sprintf(get_lang('LoginX'), $user_info['username']);
$username_span = Display::tag(
'span',
$user_info['complete_name'],
@ -1977,7 +1974,8 @@ class AnnouncementManager
if (api_is_allowed_to_edit(false, true) ||
(api_is_session_general_coach() && api_is_element_in_the_session(TOOL_ANNOUNCEMENT, $row['id'])) ||
(api_get_course_setting('allow_user_edit_announcement') && !api_is_anonymous()) ||
($row['to_group_id'] == $group_id && $isTutor)
($row['to_group_id'] == $group_id && $isTutor) ||
($session_id && api_is_coach() && api_get_configuration_value('allow_coach_to_edit_announcements'))
) {
if ($disableEdit === true) {
$modify_icons = "<a href='#'>".$editIconDisable."</a>";
@ -1985,16 +1983,17 @@ class AnnouncementManager
$modify_icons = "<a href=\"".$actionUrl."&action=modify&id=".$row['id']."\">".$editIcon."</a>";
}
$image_visibility = 'invisible';
$alt_visibility = get_lang('Visible');
if ($row['visibility'] == 1) {
$image_visibility = "visible";
$image_visibility = 'visible';
$alt_visibility = get_lang('Hide');
} else {
$image_visibility = "invisible";
$alt_visibility = get_lang('Visible');
}
$modify_icons .= "<a href=\"".$actionUrl."&action=showhide&id=".$row['id']."&sec_token=".$stok."\">".
Display::return_icon($image_visibility.'.png', $alt_visibility, '', ICON_SIZE_SMALL)."</a>";
$modify_icons .= "<a
href=\"".$actionUrl."&action=showhide&id=".$row['id']."&sec_token=".$stok."\">".
Display::return_icon($image_visibility.'.png', $alt_visibility, '', ICON_SIZE_SMALL).
"</a>";
// DISPLAY MOVE UP COMMAND only if it is not the top announcement
if ($iterator != 1) {
@ -2003,12 +2002,14 @@ class AnnouncementManager
} else {
$modify_icons .= Display::return_icon('up_na.gif', get_lang('Up'));
}
if ($iterator < $bottomAnnouncement) {
$modify_icons .= "<a href=\"".$actionUrl."&action=move&down=".$row["id"]."&sec_token=".$stok."\">".
Display::return_icon('down.gif', get_lang('Down'))."</a>";
} else {
$modify_icons .= Display::return_icon('down_na.gif', get_lang('Down'));
}
if (api_is_allowed_to_edit(false, true)) {
if ($disableEdit === true) {
$modify_icons .= Display::url($deleteIconDisable, '#');

@ -82,6 +82,16 @@ class CoursesAndSessionsCatalog
// Check special courses
$courseListToAvoid = CourseManager::get_special_course_list();
$categoryToAvoid = api_get_configuration_value('course_category_code_to_use_as_model');
if (!empty($categoryToAvoid) && api_is_student()) {
$coursesInCategoryToAvoid = CourseCategory::getCoursesInCategory($categoryToAvoid, '', false);
if (!empty($coursesInCategoryToAvoid)) {
foreach ($coursesInCategoryToAvoid as $courseToAvoid) {
$courseListToAvoid[] = $courseToAvoid['id'];
}
}
}
// Checks "hide_from_catalog" extra field
$extraFieldType = ExtraField::COURSE_FIELD_TYPE;

@ -267,14 +267,9 @@ class MoodleImport
$questionsValues = $this->readMainQuestionsXml($questionsXml, $question['questionid']);
$moduleValues['question_instances'][$index] = $questionsValues;
// Set Question Type from Moodle XML element <qtype>
$qType = $moduleValues['question_instances'][$index]['qtype'];
// Add the matched chamilo question type to the array
$moduleValues['question_instances'][$index]['chamilo_qtype'] =
$this->matchMoodleChamiloQuestionTypes($qType);
$questionInstance = Question::getInstance(
$moduleValues['question_instances'][$index]['chamilo_qtype']
);
$qType = $questionsValues['qtype'];
$questionType = $this->matchMoodleChamiloQuestionTypes($questionsValues);
$questionInstance = Question::getInstance($questionType);
if (empty($questionInstance)) {
continue;
}
@ -282,8 +277,8 @@ class MoodleImport
error_log('question: '.$question['questionid']);
}
$questionInstance->updateTitle($moduleValues['question_instances'][$index]['name']);
$questionText = $moduleValues['question_instances'][$index]['questiontext'];
$questionInstance->updateTitle($questionsValues['name']);
$questionText = $questionsValues['questiontext'];
// Replace the path from @@PLUGINFILE@@ to a correct chamilo path
$questionText = str_replace(
@ -608,7 +603,7 @@ class MoodleImport
$questionType = '';
foreach ($question->childNodes as $item) {
$currentItem[$item->nodeName] = $item->nodeValue;
if ($item->nodeName == 'qtype') {
if ('qtype' === $item->nodeName) {
$questionType = $item->nodeValue;
}
@ -619,11 +614,10 @@ class MoodleImport
$answer = $item->getElementsByTagName($this->getQuestionTypeAnswersTag($questionType));
$currentItem['plugin_qtype_'.$questionType.'_question'] = [];
for ($i = 0; $i <= $answer->length - 1; $i++) {
$currentItem['plugin_qtype_'.$questionType.'_question'][$i]['answerid'] =
$answer->item($i)->getAttribute('id');
$label = 'plugin_qtype_'.$questionType.'_question';
$currentItem[$label][$i]['answerid'] = $answer->item($i)->getAttribute('id');
foreach ($answer->item($i)->childNodes as $properties) {
$currentItem['plugin_qtype_'.$questionType.'_question'][$i][$properties->nodeName] =
$properties->nodeValue;
$currentItem[$label][$i][$properties->nodeName] = $properties->nodeValue;
}
}
@ -631,8 +625,7 @@ class MoodleImport
for ($i = 0; $i <= $typeValues->length - 1; $i++) {
foreach ($typeValues->item($i)->childNodes as $properties) {
$currentItem[$questionType.'_values'][$properties->nodeName] = $properties->nodeValue;
if ($properties->nodeName != 'sequence') {
if ($properties->nodeName !== 'sequence') {
continue;
}
@ -689,12 +682,24 @@ class MoodleImport
}
/**
* @param string $moodleQuestionType
* @param array Result of readMainQuestionsXml
*
* @return int Chamilo question type
*/
public function matchMoodleChamiloQuestionTypes($moodleQuestionType)
public function matchMoodleChamiloQuestionTypes($questionsValues)
{
$moodleQuestionType = $questionsValues['qtype'];
$questionOptions = $moodleQuestionType.'_values';
// Check <single> located in <plugin_qtype_multichoice_question><multichoice><single><single>
if (
'multichoice' === $moodleQuestionType &&
isset($questionsValues[$questionOptions]) &&
isset($questionsValues[$questionOptions]['single']) &&
1 === (int) $questionsValues[$questionOptions]['single']
) {
return UNIQUE_ANSWER;
}
switch ($moodleQuestionType) {
case 'multichoice':
return MULTIPLE_ANSWER;
@ -705,7 +710,7 @@ class MoodleImport
case 'essay':
return FREE_ANSWER;
case 'truefalse':
return UNIQUE_ANSWER_NO_OPTION;
return UNIQUE_ANSWER;
}
}

File diff suppressed because it is too large Load Diff

@ -97,7 +97,7 @@ class SortableTableFromArrayConfig extends SortableTable
*/
public function get_total_number_of_items()
{
if (isset($this->total_number_of_items) && !empty($this->total_number_of_items)) {
if (!empty($this->total_number_of_items) && $this->total_number_of_items !== -1) {
return $this->total_number_of_items;
} else {
if (!empty($this->table_data)) {

@ -1,4 +1,5 @@
<?php
/* For licensing terms, see /license.txt */
/**
@ -154,8 +155,9 @@ class Agenda
*/
public function setType($type)
{
$type = (string) trim($type);
$typeList = $this->getTypes();
if (in_array($type, $typeList)) {
if (in_array($type, $typeList, true)) {
$this->type = $type;
}
}

@ -229,6 +229,7 @@ define('LOG_EXERCISE_RESULT_DELETE', 'exe_result_deleted');
define('LOG_EXERCISE_ATTEMPT_DELETE', 'exe_attempt_deleted');
define('LOG_LP_ATTEMPT_DELETE', 'lp_attempt_deleted');
define('LOG_QUESTION_RESULT_DELETE', 'qst_attempt_deleted');
define('LOG_QUESTION_SCORE_UPDATE', 'score_attempt_updated');
define('LOG_MY_FOLDER_CREATE', 'my_folder_created');
define('LOG_MY_FOLDER_CHANGE', 'my_folder_changed');
@ -3786,7 +3787,7 @@ function api_is_allowed($tool, $action, $task_id = 0)
// Getting the permissions of the task.
if ($task_id != 0) {
$task_permissions = get_permissions('task', $task_id);
/* !!! */$_SESSION['total_permissions'][$_course['code']] = $task_permissions;
$_SESSION['total_permissions'][$_course['code']] = $task_permissions;
}
//print_r($_SESSION['total_permissions']);

@ -309,14 +309,18 @@ class Auth
// protect variables
$current_user_id = api_get_user_id();
$course_code = Database::escape_string($course_code);
$result = true;
$courseInfo = api_get_course_info($course_code);
// Check if course can be unsubscribe
if (empty($courseInfo) || empty($current_user_id)) {
return false;
}
// Check if course can be unsubscribe.
if ('1' !== $courseInfo['unsubscribe']) {
return false;
}
$courseId = $courseInfo['real_id'];
// we check (once again) if the user is not course administrator
@ -329,6 +333,8 @@ class Auth
status='1' ";
$result_check = Database::query($sql);
$number_of_rows = Database::num_rows($result_check);
$result = true;
if ($number_of_rows > 0) {
$result = false;
}

@ -1,4 +1,5 @@
<?php
/* For licensing terms, see /license.txt */
use Fhaculty\Graph\Graph;
@ -650,6 +651,10 @@ class Career extends Model
font-size: 11px;
height: 40px;
}
.panel-body{
min-height: 55px;
}
</style>';
// Create groups
@ -919,6 +924,10 @@ class Career extends Model
$iconData['Description'] = 'Result Id = '.$resultId;
}
if ('Joe Anonymous' === $iconData['TeacherUsername']) {
$iconData['TeacherUsername'] = '';
}
if (!empty($icon)) {
$params = [
'id' => 'course_'.$id.'_'.$resultId,
@ -942,7 +951,7 @@ class Career extends Model
}
if (!empty($results)) {
$content .= '<div class="row"></div><div class="pull-right">'.$results.'</div>';
$content .= '<div class="row"></div><div class="pull-left">'.$results.'</div>';
}
}

@ -151,9 +151,11 @@ class CourseManager
* @param int $orderby The column we want to order it by. Optional, defaults to first column.
* @param string $orderdirection The direction of the order (ASC or DESC). Optional, defaults to ASC.
* @param int $visibility the visibility of the course, or all by default
* @param string $startwith If defined, only return results for which the course *title* begins with this string
* @param string $startwith If defined, only return results for which the course *title* begins with this
* string
* @param string $urlId The Access URL ID, if using multiple URLs
* @param bool $alsoSearchCode An extension option to indicate that we also want to search for course codes (not *only* titles)
* @param bool $alsoSearchCode An extension option to indicate that we also want to search for course codes
* (not *only* titles)
* @param array $conditionsLike
* @param array $onlyThisCourseList
*
@ -530,13 +532,57 @@ class CourseManager
);
}
}
$subscriptionSettings = learnpath::getSubscriptionSettings();
if ($subscriptionSettings['allow_add_users_to_lp_category']) {
$em = Database::getManager();
$repo = $em->getRepository('ChamiloCourseBundle:CLpCategory');
if (api_get_configuration_value('allow_session_lp_category')) {
//$criteria = ['cId' => $course_id, 'sessionId' => $session_id];
$table = Database::get_course_table('lp_category');
$conditionSession = api_get_session_condition($session_id, true);
$sql = "SELECT * FROM $table WHERE c_id = $course_id $conditionSession";
$result = Database::query($sql);
$categories = [];
if (Database::num_rows($result)) {
while ($row = Database::fetch_array($result)) {
$categories[] = $repo->find($row['iid']);
}
}
} else {
$criteria = ['cId' => $course_id];
$categories = $repo->findBy($criteria);
}
if (!empty($categories)) {
/** @var \Chamilo\CourseBundle\Entity\CLpCategory $category */
foreach ($categories as $category) {
if ($category->getUsers()->count() > 0) {
foreach ($userList as $uid) {
$user = api_get_user_entity($uid);
$criteria = Criteria::create()->where(
Criteria::expr()->eq('user', $user)
);
$userCategory = $category->getUsers()->matching($criteria)->first();
if ($userCategory) {
$category->removeUsers($userCategory);
}
}
$em->persist($category);
$em->flush();
}
}
}
}
if (api_get_configuration_value('catalog_course_subscription_in_user_s_session')) {
// Also unlink the course from the users' currently accessible sessions
/** @var Course $course */
$course = Database::getManager()->getRepository('ChamiloCoreBundle:Course')->findOneBy([
'code' => $course_code,
]);
if (is_null($course)) {
if (null === $course) {
return false;
}
/** @var Chamilo\UserBundle\Entity\User $user */
@ -1432,8 +1478,8 @@ class CourseManager
* @param int $sessionId
* @param string $limit
* @param string $order_by the field to order the users by.
* Valid values are 'lastname', 'firstname', 'username', 'email', 'official_code' OR a part of a SQL statement
* that starts with ORDER BY ...
* Valid values are 'lastname', 'firstname', 'username', 'email',
* 'official_code' OR a part of a SQL statement that starts with ORDER BY ...
* @param int|null $filter_by_status if using the session_id: 0 or 2 (student, coach),
* if using session_id = 0 STUDENT or COURSEMANAGER
* @param bool|null $return_count
@ -2959,7 +3005,6 @@ class CourseManager
*/
public static function get_special_course_list()
{
$courseTable = Database::get_main_table(TABLE_MAIN_COURSE);
$tbl_course_field = Database::get_main_table(TABLE_EXTRA_FIELD);
$tbl_course_field_value = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
$tbl_url_course = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
@ -2970,8 +3015,7 @@ class CourseManager
// Get special course field
$sql = "SELECT id FROM $tbl_course_field
WHERE extra_field_type = $extraFieldType AND
variable = 'special_course'";
WHERE extra_field_type = $extraFieldType AND variable = 'special_course'";
$result = Database::query($sql);
if (Database::num_rows($result) > 0) {
@ -2986,22 +3030,21 @@ class CourseManager
while ($row = Database::fetch_assoc($result)) {
$courseList[] = $row['cid'];
}
if (count($courseList) < 1) {
return $courseList;
if (empty($courseList)) {
return [];
}
if (api_get_multiple_access_url()) {
//we filter the courses by the active URL
$coursesSelect = '';
if (count($courseList) == 1) {
if (count($courseList) === 1) {
$coursesSelect = $courseList[0];
} else {
$coursesSelect = implode(',', $courseList);
}
$access_url_id = api_get_current_access_url_id();
if ($access_url_id != -1) {
$urlId = api_get_current_access_url_id();
if ($urlId != -1) {
$courseList = [];
$sql = "SELECT c_id FROM $tbl_url_course
WHERE access_url_id = $access_url_id
AND c_id IN ($coursesSelect)";
WHERE access_url_id = $urlId AND c_id IN ($coursesSelect)";
$result = Database::query($sql);
while ($row = Database::fetch_assoc($result)) {
$courseList[] = $row['c_id'];
@ -3073,7 +3116,8 @@ class CourseManager
* @param int $user_id
* @param bool $include_sessions Whether to include courses from session or not
* @param bool $adminGetsAllCourses If the user is platform admin,
* whether he gets all the courses or just his. Note: This does *not* include all sessions
* whether he gets all the courses or just his. Note: This does
* *not* include all sessions
* @param bool $loadSpecialCourses
* @param array $skipCourseList List of course ids to skip
* @param bool $useUserLanguageFilterIfAvailable
@ -4758,8 +4802,8 @@ class CourseManager
* Creates a new course code based in a given code.
*
* @param string wanted code
* <code> $wanted_code = 'curse' if there are in the DB codes like curse1 curse2 the function will return: course3</code>
* if the course code doest not exist in the DB the same course code will be returned
* <code> $wanted_code = 'curse' if there are in the DB codes like curse1 curse2 the function will return:
* course3</code> if the course code doest not exist in the DB the same course code will be returned
*
* @return string wanted unused code
*/
@ -5854,6 +5898,12 @@ class CourseManager
$courseSettings[] = 'lp_return_link';
}
if (api_get_configuration_value('allow_portfolio_tool')) {
$courseSettings[] = 'qualify_portfolio_item';
$courseSettings[] = 'qualify_portfolio_comment';
$courseSettings[] = 'portfolio_max_score';
}
if (!empty($pluginCourseSettings)) {
$courseSettings = array_merge(
$courseSettings,

@ -503,7 +503,6 @@ class CourseCategory
$deleteUrl = $mainUrl.'&id='.$category['code'].'&action=delete';
$actions = [];
if ($urlId == $category['access_url_id']) {
$actions[] = Display::url($editIcon, $editUrl);
$actions[] = Display::url($moveIcon, $moveUrl);
@ -521,7 +520,7 @@ class CourseCategory
$url
);
$countCourses = self::countCoursesInCategory($category['code'], null, false);
$countCourses = self::countCoursesInCategory($category['code'], null, false, false);
$content = [
$title,
@ -597,8 +596,9 @@ class CourseCategory
/**
* @param string $category_code
* @param string $keyword
* @paran bool $avoidCourses
* @paran array $conditions
* @param bool $avoidCourses
* @param bool $checkHidePrivate
* @param array $conditions
*
* @return int
*/
@ -606,12 +606,20 @@ class CourseCategory
$category_code = '',
$keyword = '',
$avoidCourses = true,
$checkHidePrivate = true,
$conditions = []
) {
return self::getCoursesInCategory($category_code, $keyword, $avoidCourses, $conditions, true);
return self::getCoursesInCategory(
$category_code,
$keyword,
$avoidCourses,
$checkHidePrivate,
$conditions,
true
);
}
public static function getCoursesInCategory($category_code = '', $keyword = '', $avoidCourses = true, $conditions = [], $getCount = false)
public static function getCoursesInCategory($category_code = '', $keyword = '', $avoidCourses = true, $checkHidePrivate = true, $conditions = [], $getCount = false)
{
$tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
$categoryCode = Database::escape_string($category_code);
@ -621,8 +629,7 @@ class CourseCategory
if ($avoidCourses) {
$avoidCoursesCondition = CoursesAndSessionsCatalog::getAvoidCourseCondition();
}
$visibilityCondition = CourseManager::getCourseVisibilitySQLCondition('course', true);
$visibilityCondition = CourseManager::getCourseVisibilitySQLCondition('course', true, $checkHidePrivate);
$sqlInjectJoins = '';
$where = ' AND 1 = 1 ';

@ -1,4 +1,5 @@
<?php
/* For licensing terms, see /license.txt */
use Chamilo\CourseBundle\Entity\CLpCategory;
@ -529,7 +530,11 @@ class CourseHome
category = "authoring" OR
category = "interaction" OR
category = "plugin"
) OR (t.name = "'.TOOL_TRACKING.'")
) OR
(t.name = "'.TOOL_TRACKING.'")
OR (
image = "scormbuilder.gif"
)
)';
}
@ -561,7 +566,6 @@ class CourseHome
ON (t.c_id = lc.c_id AND l.category_id = lc.iid)
$conditions AND
t.c_id = $course_id $condition_session
ORDER BY
CASE WHEN l.category_id IS NULL THEN 0 ELSE 1 END,
CASE WHEN l.display_order IS NULL THEN 0 ELSE 1 END,
@ -655,6 +659,7 @@ class CourseHome
});
$isAllowToEdit = api_is_allowed_to_edit(null, true);
$showInvisibleLpsForStudents = api_get_configuration_value('show_invisible_lp_in_course_home');
foreach ($tools as $temp_row) {
$add = false;
if ($check) {
@ -665,7 +670,10 @@ class CourseHome
$add = true;
}
if ($allowEditionInSession && !empty($sessionId)) {
if (false === $showInvisibleLpsForStudents &&
false === $isAllowToEdit &&
$allowEditionInSession && !empty($sessionId)
) {
// Checking if exist row in session
$criteria = [
'cId' => $course_id,
@ -674,12 +682,10 @@ class CourseHome
];
/** @var CTool $toolObj */
$toolObj = Database::getManager()->getRepository('ChamiloCourseBundle:CTool')->findOneBy($criteria);
if ($toolObj) {
if ($isAllowToEdit == false && $toolObj->getVisibility() == false) {
if ($toolObj && $toolObj->getVisibility() == false) {
continue;
}
}
}
switch ($temp_row['image']) {
case 'scormbuilder.gif':
@ -693,6 +699,9 @@ class CourseHome
if ($isAllowToEdit) {
$add = true;
} else {
if ($showInvisibleLpsForStudents) {
$add = true;
} else {
$add = learnpath::is_lp_visible_for_student(
$lpId,
@ -700,19 +709,25 @@ class CourseHome
$courseInfo,
$sessionId
);
// Check if LP is visible.
$visibility = api_get_item_visibility($courseInfo, TOOL_LEARNPATH, $lpId, $sessionId);
if (1 !== $visibility) {
$add = false;
}
}
}
if ($path) {
$temp_row['custom_image'] = $path;
}
break;
case 'lp_category.gif':
$lpCategory = self::getPublishedLpCategoryFromLink(
$temp_row['link']
);
$add = learnpath::categoryIsVisibleForStudent(
$lpCategory,
$user
);
$lpCategory = self::getPublishedLpCategoryFromLink($temp_row['link']);
if ($showInvisibleLpsForStudents) {
$add = true;
} else {
$add = learnpath::categoryIsVisibleForStudent($lpCategory, $user);
}
break;
}
@ -802,6 +817,15 @@ class CourseHome
$properties['name'] = $links_row['title'];
$properties['session_id'] = $links_row['session_id'];
$properties['link'] = $links_row['url'];
// For students, check if link is visible in the session.
if ($sessionId && !($is_platform_admin || api_is_course_admin())) {
$visibility = api_get_item_visibility($courseInfo, TOOL_LINK, $links_row['iid'], $sessionId);
if (1 !== $visibility) {
continue;
}
}
$properties['visibility'] = $links_row['visibility'];
$properties['image'] = $links_row['visibility'] == '0' ? 'file_html.png' : 'file_html.png';
$properties['adminlink'] = $linkUrl.'&id='.$links_row['id'].'&cidReq='.$courseInfo['code'];
@ -892,6 +916,7 @@ class CourseHome
$items = [];
$app_plugin = new AppPlugin();
$toolRepo = Database::getManager()->getRepository('ChamiloCourseBundle:CTool');
if (isset($all_tools_list)) {
$lnk = '';
foreach ($all_tools_list as &$tool) {
@ -905,9 +930,56 @@ class CourseHome
$item = [];
$studentview = false;
$tool['original_link'] = $tool['link'];
if ($tool['image'] === 'lp_category.gif') {
if ($session_id) {
if (api_is_coach() || api_is_allowed_to_edit()) {
$lpCategory = self::getPublishedLpCategoryFromLink($tool['link']);
$itemInfo = api_get_item_property_info(
$courseId,
TOOL_LEARNPATH_CATEGORY,
$lpCategory->getId(),
$session_id
);
if ($itemInfo && 0 === (int) $itemInfo['visibility']) {
$tool['image'] = 'lp_category_na.gif';
}
} else {
$categoryInSessionName = str_replace('id_session', $session_id, $tool['name']);
$criteria = [
'cId' => $courseId,
'name' => $categoryInSessionName,
'sessionId' => $session_id,
];
/** @var CTool $tool */
$toolObj = $toolRepo->findOneBy($criteria);
if ($toolObj) {
$visibility = (int) $toolObj->getVisibility();
if (0 === $visibility) {
continue;
}
}
}
}
}
if ($tool['image'] === 'scormbuilder.gif') {
// Check if the published learnpath is visible for student
$lpId = self::getPublishedLpIdFromLink($tool['link']);
if ($session_id && api_is_coach()) {
$itemInfo = api_get_item_property_info(
$courseId,
TOOL_LEARNPATH,
$lpId,
$session_id
);
if ($itemInfo && 0 === (int) $itemInfo['visibility']) {
$tool['image'] = 'scormbuilder_na.gif';
}
}
if (api_is_allowed_to_edit(null, true)) {
$studentview = true;
}
@ -937,18 +1009,13 @@ class CourseHome
$item['extra'] = null;
$toolAdmin = isset($tool['admin']) ? $tool['admin'] : '';
$extraClass = '';
$isSessionToolVisible = false;
if ($is_allowed_to_edit && $allowChangeVisibility) {
if (empty($session_id)) {
if (isset($tool['id'])) {
if ($tool['visibility'] == '1' && $toolAdmin != '1') {
$link['name'] = Display::return_icon(
'visible.png',
get_lang('Deactivate'),
['id' => 'linktool_'.$tool['iid']],
ICON_SIZE_SMALL,
false
);
$isSessionToolVisible = true;
$link['name'] = '<em
id="'.'linktool_'.$tool['iid'].'"
class="fa fa-eye"
@ -957,18 +1024,13 @@ class CourseHome
$lnk[] = $link;
}
if ($tool['visibility'] == '0' && $toolAdmin != '1') {
$link['name'] = Display::return_icon(
'invisible.png',
get_lang('Activate'),
['id' => 'linktool_'.$tool['iid']],
ICON_SIZE_SMALL,
false
);
$isSessionToolVisible = false;
$link['name'] = '<em
id="'.'linktool_'.$tool['iid'].'"
class="fa fa-eye-slash text-muted"
title="'.get_lang('Activate').'"></em>';
$link['cmd'] = 'restore=yes';
$extraClass = 'text-muted';
$lnk[] = $link;
}
}
@ -978,58 +1040,42 @@ class CourseHome
'name' => $tool['name'],
'sessionId' => $session_id,
];
/** @var CTool $tool */
$toolObj = Database::getManager()->getRepository('ChamiloCourseBundle:CTool')->findOneBy($criteria);
$toolObj = $toolRepo->findOneBy($criteria);
if ($toolObj) {
$visibility = (int) $toolObj->getVisibility();
switch ($visibility) {
case 0:
$isSessionToolVisible = false;
if (in_array($tool['image'], ['scormbuilder.png', 'scormbuilder.gif'])) {
$info = pathinfo($tool['image']);
$basename = basename($tool['image'], '.'.$info['extension']);
$tool['image'] = $basename.'_na.'.$info['extension'];
$link['name'] = Display::return_icon(
'invisible.png',
get_lang('Activate'),
['id' => 'linktool_'.$tool['iid']],
ICON_SIZE_SMALL,
false
);
}
$link['name'] = '<em
id="'.'linktool_'.$tool['iid'].'"
class="fa fa-eye-slash text-muted"
title="'.get_lang('Deactivate').'"></em>';
title="'.get_lang('Activate').'"></em>';
$link['cmd'] = 'restore=yes';
$extraClass = 'text-muted';
$lnk[] = $link;
break;
case 1:
$link['name'] = Display::return_icon(
'visible.png',
get_lang('Deactivate'),
['id' => 'linktool_'.$tool['iid']],
ICON_SIZE_SMALL,
false
);
$isSessionToolVisible = true;
$link['name'] = '<em
id="'.'linktool_'.$tool['iid'].'"
class="fa fa-eye"
title="'.get_lang('Activate').'"></em>';
title="'.get_lang('Deactivate').'"></em>';
$link['cmd'] = 'hide=yes';
$lnk[] = $link;
break;
}
} else {
$link['name'] = Display::return_icon(
'visible.png',
get_lang('Deactivate'),
['id' => 'linktool_'.$tool['iid']],
ICON_SIZE_SMALL,
false
);
$isSessionToolVisible = true;
$link['name'] = '<em
id="'.'linktool_'.$tool['iid'].'"
class="fa fa-eye"
title="'.get_lang('Activate').'"></em>';
title="'.get_lang('Deactivate').'"></em>';
$link['cmd'] = 'hide=yes';
$lnk[] = $link;
}
@ -1069,12 +1115,35 @@ class CourseHome
}
$class = '';
if ($tool['visibility'] == '0' && $toolAdmin != '1') {
$class = 'text-muted';
$info = pathinfo($tool['image']);
$basename = basename($tool['image'], '.'.$info['extension']);
if ($tool['visibility'] == '0' && $toolAdmin != '1') {
$class = 'text-muted';
if (!strpos($tool['image'], '_na')) {
$tool['image'] = $basename.'_na.'.$info['extension'];
}
}
if ($is_allowed_to_edit && $allowChangeVisibility &&
($tool['image'] === 'scormbuilder.gif' || $tool['image'] === 'scormbuilder_na.gif')
) {
if (false === $isSessionToolVisible) {
$tool['image'] = 'scormbuilder_na.gif';
} else {
$tool['image'] = 'scormbuilder.gif';
}
}
if ($is_allowed_to_edit && $allowChangeVisibility &&
($tool['image'] === 'lp_category.gif' || $tool['image'] === 'lp_category_na.gif')
) {
if (false === $isSessionToolVisible) {
$tool['image'] = 'lp_category_na.gif';
} else {
$tool['image'] = 'lp_category.gif';
}
}
$qm_or_amp = strpos($tool['link'], '?') === false ? '?' : '&';
@ -1082,8 +1151,10 @@ class CourseHome
if ($tool['image'] === 'file_html.png' || $tool['image'] === 'file_html_na.png') {
$tool['link'] = $tool['link'];
} else {
if (!in_array($tool['image'], ['lp_category.gif', 'lp_category_na.gif'])) {
$tool['link'] = $tool['link'].$qm_or_amp.api_get_cidreq(true, false).'&gidReq=0';
}
}
$toolIid = isset($tool['iid']) ? $tool['iid'] : null;
@ -1110,7 +1181,7 @@ class CourseHome
$tool_link_params = [
'id' => 'tooldesc_'.$toolIid,
'href' => $tool['link'],
'class' => $class,
'class' => "$class $extraClass ",
'target' => $tool['target'],
];
}
@ -1160,18 +1231,6 @@ class CourseHome
false
);
/*if (!empty($tool['custom_icon'])) {
$image = self::getCustomWebIconPath().$tool['custom_icon'];
$icon = Display::img(
$image,
$tool['description'],
array(
'class' => 'tool-icon',
'id' => 'toolimage_'.$tool['id']
)
);
}*/
// Validation when belongs to a session
$session_img = api_get_session_image(
$tool['session_id'],
@ -1202,7 +1261,6 @@ class CourseHome
$originalImage = self::getToolIcon($item, ICON_SIZE_BIG);
$item['tool']['only_icon_medium'] = self::getToolIcon($item, ICON_SIZE_MEDIUM, false);
$item['tool']['only_icon_small'] = self::getToolIcon($item, ICON_SIZE_SMALL, false);
if ($theme === 'activity_big') {
$item['tool']['image'] = Display::url(
$originalImage,
@ -1698,11 +1756,9 @@ class CourseHome
// Adding only maintenance for coaches.
$myList = self::get_tools_category(TOOL_ADMIN_PLATFORM);
$onlyMaintenanceList = [];
foreach ($myList as $item) {
if ($item['name'] === 'course_maintenance') {
$item['link'] = 'course_info/maintenance_coach.php';
$onlyMaintenanceList[] = $item;
}
}

@ -2529,6 +2529,11 @@ class Display
}
$title = !empty($title) ? '<div class="panel-heading" '.$headerStyle.' ><h3 class="panel-title">'.$title.'</h3>'.$extra.'</div>' : '';
if (empty($title) && !empty($extra)) {
$title = '<div class="panel-heading" '.$headerStyle.' >'.$extra.'</div>';
}
$footer = !empty($footer) ? '<div class="panel-footer">'.$footer.'</div>' : '';
$typeList = ['primary', 'success', 'info', 'warning', 'danger'];
$style = !in_array($type, $typeList) ? 'default' : $type;
@ -2865,7 +2870,7 @@ HTML;
*
* @return string
*/
public static function getFrameReadyBlock($frameName)
public static function getFrameReadyBlock($frameName, $itemType = '')
{
$webPublicPath = api_get_path(WEB_PUBLIC_PATH);
$webJsPath = api_get_path(WEB_LIBRARY_JS_PATH);
@ -2932,13 +2937,13 @@ HTML;
});
},
"'.$frameName.'",
[
{type:"script", src:"'.api_get_jquery_web_path().'", deps: [
';
if ('quiz' === $itemType) {
$jquery = '
'.$fixLink.'
{type:"script", src:"'.api_get_path(WEB_LIBRARY_PATH).'javascript/jquery.highlight.js"},
{type:"script", src:"'.api_get_path(WEB_CODE_PATH).'glossary/glossary.js.php?'.api_get_cidreq().'"},
{type:"script", src:"'.$webPublicPath.'assets/jquery-ui/jquery-ui.min.js"},
{type:"script", src: "'.$webPublicPath.'assets/mediaelement/build/mediaelement-and-player.min.js",
deps: [
{type:"script", src: "'.$webJsPath.'mediaelement/plugins/vrview/vrview.js"},
@ -2946,7 +2951,27 @@ HTML;
'.$videoPluginFiles.'
]},
'.$translateHtml.'
';
} else {
$jquery = '
{type:"script", src:"'.api_get_jquery_web_path().'", deps: [
'.$fixLink.'
{type:"script", src:"'.api_get_path(WEB_LIBRARY_PATH).'javascript/jquery.highlight.js"},
{type:"script", src:"'.api_get_path(WEB_CODE_PATH).'glossary/glossary.js.php?'.api_get_cidreq().'"},
{type:"script", src:"'.$webPublicPath.'assets/jquery-ui/jquery-ui.min.js"},
{type:"script", src: "'.$webPublicPath.'assets/mediaelement/build/mediaelement-and-player.min.js",
deps: [
{type:"script", src: "'.$webJsPath.'mediaelement/plugins/vrview/vrview.js"},
{type:"script", src: "'.$webJsPath.'mediaelement/plugins/markersrolls/markersrolls.min.js"},
'.$videoPluginFiles.'
]},
'.$translateHtml.'
]},';
}
$frameReady .= '
[
'.$jquery.'
'.$videoPluginCssFiles.'
{type:"script", src:"'.$webPublicPath.'assets/MathJax/MathJax.js?config=AM_HTMLorMML"},
{type:"stylesheet", src:"'.$webPublicPath.'assets/jquery-ui/themes/smoothness/jquery-ui.min.css"},

@ -3531,10 +3531,16 @@ class DocumentManager
// If you want to debug it, I advise you to do "echo" on the eval statements.
$newResources = [];
$added = [];
if (!empty($resources) && $user_in_course) {
foreach ($resources as $resource) {
$docId = $resource['id'];
if (in_array($docId, $added)) {
continue;
}
$is_visible = self::is_visible_by_id(
$resource['id'],
$docId,
$course_info,
$session_id,
api_get_user_id()
@ -3545,7 +3551,7 @@ class DocumentManager
continue;
}
}
$added[] = $docId;
$newResources[] = $resource;
}
}
@ -5158,7 +5164,6 @@ class DocumentManager
$filetype = $document_data['filetype'];
$path = $document_data['path'];
$url_path = urlencode($document_data['path']);
$basePageUrl = api_get_path(WEB_CODE_PATH).'document/';
$pageUrl = $basePageUrl.'document.php';
@ -5171,19 +5176,19 @@ class DocumentManager
if (!$show_as_icon) {
// Build download link (icon)
$forcedownload_link = $filetype == 'folder'
$forcedownload_link = $filetype === 'folder'
? $pageUrl.'?'.$courseParams.'&action=downloadfolder&id='.$document_data['id']
: $pageUrl.'?'.$courseParams.'&amp;action=download&amp;id='.$document_data['id'];
// Folder download or file download?
$forcedownload_icon = $filetype == 'folder' ? 'save_pack.png' : 'save.png';
$forcedownload_icon = $filetype === 'folder' ? 'save_pack.png' : 'save.png';
// Prevent multiple clicks on zipped folder download
$prevent_multiple_click = $filetype == 'folder' ? " onclick=\"javascript: if(typeof clic_$dbl_click_id == 'undefined' || !clic_$dbl_click_id) { clic_$dbl_click_id=true; window.setTimeout('clic_".($dbl_click_id++)."=false;',10000); } else { return false; }\"" : '';
$prevent_multiple_click = $filetype === 'folder' ? " onclick=\"javascript: if(typeof clic_$dbl_click_id == 'undefined' || !clic_$dbl_click_id) { clic_$dbl_click_id=true; window.setTimeout('clic_".($dbl_click_id++)."=false;',10000); } else { return false; }\"" : '';
}
$target = '_self';
$is_browser_viewable_file = false;
if ($filetype == 'file') {
if ($filetype === 'file') {
// Check the extension
$ext = explode('.', $path);
$ext = strtolower($ext[count($ext) - 1]);
@ -5191,11 +5196,7 @@ class DocumentManager
// HTML-files an some other types are shown in a frameset by default.
$is_browser_viewable_file = self::isBrowserViewable($ext);
if ($is_browser_viewable_file) {
if ($ext == 'pdf' || in_array($ext, $webODFList)) {
$url = $pageUrl.'?'.$courseParams.'&amp;action=download&amp;id='.$document_data['id'];
} else {
$url = $basePageUrl.'showinframes.php?'.$courseParams.'&id='.$document_data['id'];
}
} else {
// url-encode for problematic characters (we may not call them dangerous characters...)
//$path = str_replace('%2F', '/', $url_path).'?'.$courseParams;
@ -5213,12 +5214,12 @@ class DocumentManager
$tooltip_title = $title;
$tooltip_title_alt = $tooltip_title;
if ($filetype == 'link') {
if ($filetype === 'link') {
$tooltip_title_alt = $title;
$url = $document_data['comment'].'" target="_blank';
}
if ($path == '/shared_folder') {
if ($path === '/shared_folder') {
$tooltip_title_alt = get_lang('UserFolders');
} elseif (strstr($path, 'shared_folder_session_')) {
$tooltip_title_alt = get_lang('UserFolders').' ('.api_get_session_name(api_get_session_id()).')';
@ -5633,7 +5634,7 @@ class DocumentManager
$parent_id
);
if (!$is_read_only /* or ($session_id!=api_get_session_id()) */) {
if (!$is_read_only) {
// Add action to covert to PDF, will create a new document whit same filename but .pdf extension
// @TODO: add prompt to select a format target
if (!in_array($path, self::get_system_folders())) {
@ -6553,7 +6554,7 @@ class DocumentManager
// checking
$table = Database::get_course_table(TABLE_DOCUMENT);
$courseId = $courseInfo['real_id'];
echo $sql = "SELECT * FROM $table WHERE id = $documentId AND c_id = $courseId";
$sql = "SELECT * FROM $table WHERE id = $documentId AND c_id = $courseId";
$result = Database::query($sql);
$exists = Database::num_rows($result) > 0;
$fileDeletedFromDb = !$exists;
@ -6647,6 +6648,12 @@ class DocumentManager
*/
public static function getFileHostingWhiteList()
{
$links = api_get_configuration_value('documents_custom_cloud_link_list');
if (!empty($links) && isset($links['links'])) {
return $links['links'];
}
return [
'asuswebstorage.com',
'box.com',

@ -532,7 +532,13 @@ class ExerciseLib
}
if ($answerType != UNIQUE_ANSWER_IMAGE) {
$answer = Security::remove_XSS($answer, STUDENT);
$userStatus = STUDENT;
// Allows to do a remove_XSS in question of exersice with user status COURSEMANAGER
// see BT#18242
if (api_get_configuration_value('question_exercise_html_strict_filtering')) {
$userStatus = COURSEMANAGERLOWSECURITY;
}
$answer = Security::remove_XSS($answer, $userStatus);
}
$s .= Display::input(
'hidden',
@ -578,7 +584,13 @@ class ExerciseLib
case GLOBAL_MULTIPLE_ANSWER:
case MULTIPLE_ANSWER_TRUE_FALSE_DEGREE_CERTAINTY:
$input_id = 'choice-'.$questionId.'-'.$answerId;
$answer = Security::remove_XSS($answer, STUDENT);
$userStatus = STUDENT;
// Allows to do a remove_XSS in question of exersice with user status COURSEMANAGER
// see BT#18242
if (api_get_configuration_value('question_exercise_html_strict_filtering')) {
$userStatus = COURSEMANAGERLOWSECURITY;
}
$answer = Security::remove_XSS($answer, $userStatus);
if (in_array($numAnswer, $userChoiceList)) {
$attributes = [
@ -775,7 +787,13 @@ class ExerciseLib
}
}
$answer = Security::remove_XSS($answer, STUDENT);
$userStatus = STUDENT;
// Allows to do a remove_XSS in question of exersice with user status COURSEMANAGER
// see BT#18242
if (api_get_configuration_value('question_exercise_html_strict_filtering')) {
$userStatus = COURSEMANAGERLOWSECURITY;
}
$answer = Security::remove_XSS($answer, $userStatus);
$answer_input = '<input type="hidden" name="choice2['.$questionId.']" value="0" />';
$answer_input .= '<label class="checkbox">';
$answer_input .= Display::input(
@ -811,7 +829,13 @@ class ExerciseLib
}
}
}
$answer = Security::remove_XSS($answer, STUDENT);
$userStatus = STUDENT;
// Allows to do a remove_XSS in question of exersice with user status COURSEMANAGER
// see BT#18242
if (api_get_configuration_value('question_exercise_html_strict_filtering')) {
$userStatus = COURSEMANAGERLOWSECURITY;
}
$answer = Security::remove_XSS($answer, $userStatus);
$s .= '<tr>';
$s .= Display::tag('td', $answer);
foreach ($objQuestionTmp->options as $key => $item) {
@ -1890,7 +1914,7 @@ HOTSPOT;
*/
public static function get_count_exam_results($exerciseId, $conditions, $courseCode = '', $showSession = false)
{
$count = self::get_exam_results_data(
return self::get_exam_results_data(
null,
null,
null,
@ -1901,8 +1925,6 @@ HOTSPOT;
$courseCode,
$showSession
);
return $count;
}
/**
@ -2048,7 +2070,7 @@ HOTSPOT;
}
/**
* Gets the exam'data results.
* Gets exercise results.
*
* @todo this function should be moved in a library + no global calls
*
@ -2095,7 +2117,6 @@ HOTSPOT;
}
$documentPath = api_get_path(SYS_COURSE_PATH).$courseInfo['path'].'/document';
$course_id = $courseInfo['real_id'];
$sessionId = api_get_session_id();
$exercise_id = (int) $exercise_id;
@ -2121,6 +2142,13 @@ HOTSPOT;
$sessionCondition = " AND ttte.session_id = $sessionId";
}
if (empty($sessionId) &&
api_get_configuration_value('show_exercise_session_attempts_in_base_course')
) {
$session_id_and = '';
$sessionCondition = '';
}
$exercise_where = '';
if (!empty($exercise_id)) {
$exercise_where .= ' AND te.exe_exo_id = '.$exercise_id.' ';
@ -2383,20 +2411,19 @@ HOTSPOT;
if (is_array($results)) {
$users_array_id = [];
$from_gradebook = false;
if (isset($_GET['gradebook']) && $_GET['gradebook'] == 'view') {
if (isset($_GET['gradebook']) && $_GET['gradebook'] === 'view') {
$from_gradebook = true;
}
$sizeof = count($results);
$locked = api_resource_is_locked_by_gradebook(
$exercise_id,
LINK_EXERCISE
);
$locked = api_resource_is_locked_by_gradebook($exercise_id, LINK_EXERCISE);
$timeNow = strtotime(api_get_utc_datetime());
// Looping results
for ($i = 0; $i < $sizeof; $i++) {
$revised = $results[$i]['revised'];
if ($results[$i]['completion_status'] == 'incomplete') {
$attemptSessionId = (int) $results[$i]['session_id'];
$cidReq = api_get_cidreq(false).'&id_session='.$attemptSessionId;
if ('incomplete' === $results[$i]['completion_status']) {
// If the exercise was incomplete, we need to determine
// if it is still into the time allowed, or if its
// allowed time has expired and it can be closed
@ -2430,17 +2457,20 @@ HOTSPOT;
$users_array_id[] = $results[$i]['username'].$results[$i]['firstname'].$results[$i]['lastname'];
}
$lp_obj = isset($results[$i]['orig_lp_id']) && isset($lp_list[$results[$i]['orig_lp_id']]) ? $lp_list[$results[$i]['orig_lp_id']] : null;
$lp_obj = isset($results[$i]['orig_lp_id']) &&
isset($lp_list[$results[$i]['orig_lp_id']]) ? $lp_list[$results[$i]['orig_lp_id']] : null;
if (empty($lp_obj)) {
// Try to get the old id (id instead of iid)
$lpNewId = isset($results[$i]['orig_lp_id']) && isset($oldIds[$results[$i]['orig_lp_id']]) ? $oldIds[$results[$i]['orig_lp_id']] : null;
$lpNewId = isset($results[$i]['orig_lp_id']) &&
isset($oldIds[$results[$i]['orig_lp_id']]) ? $oldIds[$results[$i]['orig_lp_id']] : null;
if ($lpNewId) {
$lp_obj = isset($lp_list[$lpNewId]) ? $lp_list[$lpNewId] : null;
}
}
$lp_name = null;
if ($lp_obj) {
$url = api_get_path(WEB_CODE_PATH).'lp/lp_controller.php?'.api_get_cidreq().'&action=view&lp_id='.$results[$i]['orig_lp_id'];
$url = api_get_path(WEB_CODE_PATH).
'lp/lp_controller.php?'.$cidReq.'&action=view&lp_id='.$results[$i]['orig_lp_id'];
$lp_name = Display::url(
$lp_obj['lp_name'],
$url,
@ -2508,7 +2538,7 @@ HOTSPOT;
$revisedLabel = '';
switch ($revised) {
case 0:
$actions .= "<a href='exercise_show.php?".api_get_cidreq()."&action=qualify&id=$id'>".
$actions .= "<a href='exercise_show.php?".$cidReq."&action=qualify&id=$id'>".
Display:: return_icon(
'quiz.png',
get_lang('Qualify')
@ -2520,7 +2550,7 @@ HOTSPOT;
);
break;
case 1:
$actions .= "<a href='exercise_show.php?".api_get_cidreq()."&action=edit&id=$id'>".
$actions .= "<a href='exercise_show.php?".$cidReq."&action=edit&id=$id'>".
Display:: return_icon(
'edit.png',
get_lang('Edit'),
@ -2535,11 +2565,9 @@ HOTSPOT;
break;
case 2: //finished but not marked as such
$actions .= '<a href="exercise_report.php?'
.api_get_cidreq()
.'&exerciseId='
.$exercise_id
.'&a=close&id='
.$id
.$cidReq
.'&exerciseId='.$exercise_id
.'&a=close&id='.$id
.'">'.
Display:: return_icon(
'lock.png',
@ -2569,7 +2597,7 @@ HOTSPOT;
}
if ($filter == 2) {
$actions .= ' <a href="exercise_history.php?'.api_get_cidreq().'&exe_id='.$id.'">'.
$actions .= ' <a href="exercise_history.php?'.$cidReq.'&exe_id='.$id.'">'.
Display:: return_icon(
'history.png',
get_lang('ViewHistoryChange')
@ -2588,7 +2616,7 @@ HOTSPOT;
.'</a>';
$recalculateUrl = api_get_path(WEB_CODE_PATH).'exercise/recalculate.php?'.
api_get_cidreq().'&'.
$cidReq.'&'.
http_build_query([
'id' => $id,
'exercise' => $exercise_id,
@ -2606,7 +2634,8 @@ HOTSPOT;
);
$filterByUser = isset($_GET['filter_by_user']) ? (int) $_GET['filter_by_user'] : 0;
$delete_link = '<a href="exercise_report.php?'.api_get_cidreq().'&filter_by_user='.$filterByUser.'&filter='.$filter.'&exerciseId='.$exercise_id.'&delete=delete&did='.$id.'"
$delete_link = '<a
href="exercise_report.php?'.$cidReq.'&filter_by_user='.$filterByUser.'&filter='.$filter.'&exerciseId='.$exercise_id.'&delete=delete&did='.$id.'"
onclick="javascript:if(!confirm(\''.sprintf(
addslashes(get_lang('DeleteAttempt')),
$results[$i]['username'],
@ -2629,7 +2658,7 @@ HOTSPOT;
$actions .= $delete_link;
}
} else {
$attempt_url = api_get_path(WEB_CODE_PATH).'exercise/result.php?'.api_get_cidreq().'&id='.$results[$i]['exe_id'].'&id_session='.$sessionId;
$attempt_url = api_get_path(WEB_CODE_PATH).'exercise/result.php?'.$cidReq.'&id='.$results[$i]['exe_id'];
$attempt_link = Display::url(
get_lang('Show'),
$attempt_url,
@ -2661,8 +2690,8 @@ HOTSPOT;
if ($is_allowedToEdit) {
$sessionName = '';
$sessionStartAccessDate = '';
if (!empty($results[$i]['session_id'])) {
$sessionInfo = api_get_session_info($results[$i]['session_id']);
if (!empty($attemptSessionId)) {
$sessionInfo = api_get_session_info($attemptSessionId);
if (!empty($sessionInfo)) {
$sessionName = $sessionInfo['name'];
$sessionStartAccessDate = api_get_local_time($sessionInfo['access_start_date']);
@ -2678,7 +2707,8 @@ HOTSPOT;
if (!empty($question_list)) {
foreach ($question_list as $questionId) {
$objQuestionTmp = Question::read($questionId, $objExercise->course);
// We're inside *one* question. Go through each possible answer for this question
// We're inside *one* question.
// Go through each possible answer for this question.
$result = $objExercise->manage_answer(
$exeId,
$questionId,
@ -2755,8 +2785,8 @@ HOTSPOT;
$result['total'],
true,
true,
true, // $show_only_percentage = false
true, // hide % sign
true,
true,
$decimalSeparator,
$thousandSeparator,
$roundValues
@ -3880,7 +3910,7 @@ EOT;
*
* @param int $question_id
* @param int $exercise_id
* @param string $course_code
* @param int $courseId
* @param int $session_id
* @param bool $onlyStudent Filter only enrolled students
*
@ -3889,7 +3919,7 @@ EOT;
public static function get_student_stats_by_question(
$question_id,
$exercise_id,
$course_code,
$courseId,
$session_id,
$onlyStudent = false
) {
@ -3899,15 +3929,12 @@ EOT;
$question_id = (int) $question_id;
$exercise_id = (int) $exercise_id;
$course_code = Database::escape_string($course_code);
$session_id = (int) $session_id;
$courseId = api_get_course_int_id($course_code);
$courseId = (int) $courseId;
$sql = "SELECT MAX(marks) as max, MIN(marks) as min, AVG(marks) as average
FROM $track_exercises e
";
if (true == $onlyStudent) {
$courseCondition = '';
FROM $track_exercises e ";
if ($onlyStudent) {
if (empty($session_id)) {
$courseCondition = "
INNER JOIN $courseUser c
@ -3918,16 +3945,19 @@ EOT;
AND relation_type <> 2
)";
} else {
$sessionRelCourse = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
$courseCondition = "
INNER JOIN $courseUser c
INNER JOIN $sessionRelCourse sc
ON (
e.exe_user_id = c.user_id AND
e.c_id = c.c_id AND
c.status = 0
)";
e.exe_user_id = sc.user_id AND
e.c_id = sc.c_id AND
e.session_id = sc.session_id AND
sc.status = 0
) ";
}
$sql .= $courseCondition;
}
$sql .= "
INNER JOIN $track_attempt a
ON (
@ -4163,7 +4193,7 @@ EOT;
* @param int $answer_id
* @param int $question_id
* @param int $exercise_id
* @param string $course_code
* @param int $courseId
* @param int $session_id
* @param string $question_type
* @param string $correct_answer
@ -4175,7 +4205,7 @@ EOT;
$answer_id,
$question_id,
$exercise_id,
$course_code,
$courseId,
$session_id,
$question_type = null,
$correct_answer = null,
@ -4190,7 +4220,7 @@ EOT;
$question_id = (int) $question_id;
$answer_id = (int) $answer_id;
$exercise_id = (int) $exercise_id;
$courseId = api_get_course_int_id($course_code);
$courseId = (int) $courseId;
$session_id = (int) $session_id;
switch ($question_type) {
@ -4660,7 +4690,7 @@ EOT;
}
// Check if the current attempt is the last.
if (false === $save_user_result && !empty($attempts)) {
/*if (false === $save_user_result && !empty($attempts)) {
$showTotalScoreAndUserChoicesInLastAttempt = false;
$position = 1;
foreach ($attempts as $attempt) {
@ -4673,7 +4703,7 @@ EOT;
if ($position == $objExercise->attempts) {
$showTotalScoreAndUserChoicesInLastAttempt = true;
}
}
}*/
}
}
@ -4911,6 +4941,16 @@ EOT;
}
$contents = ob_get_clean();
// Hide correct answers.
if ($scorePassed && false === $objExercise->disableHideCorrectAnsweredQuestions) {
// Skip correct answers.
$hide = (int) $objExercise->getPageConfigurationAttribute('hide_correct_answered_questions');
if (1 === $hide) {
continue;
}
}
$question_content = '';
if ($show_results) {
$question_content = '<div class="question_row_answer">';
@ -5465,22 +5505,21 @@ EOT;
/**
* Get the recorder audio component for save a teacher audio feedback.
*
* @param Template $template
* @param int $attemptId
* @param int $questionId
* @param int $userId
*
* @return string
*/
public static function getOralFeedbackForm($attemptId, $questionId, $userId)
public static function getOralFeedbackForm($template, $attemptId, $questionId, $userId)
{
$view = new Template('', false, false, false, false, false, false);
$view->assign('user_id', $userId);
$view->assign('question_id', $questionId);
$view->assign('directory', "/../exercises/teacher_audio/$attemptId/");
$view->assign('file_name', "{$questionId}_{$userId}");
$template = $view->get_template('exercise/oral_expression.tpl');
$template->assign('user_id', $userId);
$template->assign('question_id', $questionId);
$template->assign('directory', "/../exercises/teacher_audio/$attemptId/");
$template->assign('file_name', "{$questionId}_{$userId}");
return $view->fetch($template);
return $template->fetch($template->get_template('exercise/oral_expression.tpl'));
}
/**
@ -5960,23 +5999,34 @@ EOT;
return $total;
}
public static function parseContent($content, $stats, $exercise, $trackInfo, $currentUserId = 0)
public static function parseContent($content, $stats, Exercise $exercise, $trackInfo, $currentUserId = 0)
{
$wrongAnswersCount = $stats['failed_answers_count'];
$attemptDate = substr($trackInfo['exe_date'], 0, 10);
$exeId = $trackInfo['exe_id'];
$resultsStudentUrl = api_get_path(WEB_CODE_PATH).
'exercise/result.php?id='.$exeId.'&'.api_get_cidreq();
$resultsTeacherUrl = api_get_path(WEB_CODE_PATH).
'exercise/exercise_show.php?action=edit&id='.$exeId.'&'.api_get_cidreq();
$content = str_replace(
[
'((exercise_error_count))',
'((all_answers_html))',
'((all_answers_teacher_html))',
'((exercise_title))',
'((exercise_attempt_date))',
'((link_to_test_result_page_student))',
'((link_to_test_result_page_teacher))',
],
[
$wrongAnswersCount,
$stats['all_answers_html'],
$stats['all_answers_teacher_html'],
$exercise->get_formated_title(),
$attemptDate,
$resultsStudentUrl,
$resultsTeacherUrl,
],
$content
);
@ -5999,7 +6049,8 @@ EOT;
$exercise_stat_info,
$courseInfo,
$attemptCountToSend,
$stats
$stats,
$statsTeacher
) {
$notifications = api_get_configuration_value('exercise_finished_notification_settings');
if (empty($notifications)) {
@ -6011,6 +6062,7 @@ EOT;
$wrongAnswersCount = $stats['failed_answers_count'];
$exercisePassed = $stats['exercise_passed'];
$countPendingQuestions = $stats['count_pending_questions'];
$stats['all_answers_teacher_html'] = $statsTeacher['all_answers_html'];
// If there are no pending questions (Open questions).
if (0 === $countPendingQuestions) {
@ -6079,11 +6131,10 @@ EOT;
if ($extraFieldData && isset($extraFieldData['value'])) {
$content = $extraFieldData['value'];
$content = self::parseContent($content, $stats, $objExercise, $exercise_stat_info, $studentId);
if (false === $exercisePassed) {
//if (false === $exercisePassed) {
if (0 !== $wrongAnswersCount) {
$content .= $stats['failed_answers_html'];
}
}
$sendMessage = true;
if (!empty($exerciseNotification)) {

@ -3117,6 +3117,7 @@ JAVASCRIPT;
$tagRelExtraTable = Database::get_main_table(TABLE_MAIN_EXTRA_FIELD_REL_TAG);
$tagTable = Database::get_main_table(TABLE_MAIN_TAG);
$optionsTable = Database::get_main_table(TABLE_EXTRA_FIELD_OPTIONS);
$value = Database::escape_string(implode("','", $options));
$sql = "SELECT DISTINCT t.*, v.value, o.display_text
FROM $tagRelExtraTable te
@ -3126,7 +3127,7 @@ JAVASCRIPT;
ON (te.item_id = v.item_id AND v.field_id = $id)
INNER JOIN $optionsTable o
ON (o.option_value = v.value)
WHERE v.value IN ('".implode("','", $options)."')
WHERE v.value IN ('".$value."')
ORDER BY o.option_order, t.tag
";

@ -2474,7 +2474,7 @@ class GroupManager
);
$table->set_additional_parameters(['category' => $category_id]);
$column = 0;
if (api_is_allowed_to_edit(false, true) and count($group_list) > 1) {
if (api_is_allowed_to_edit(false, true) && count($group_list) > 1) {
$table->set_header($column++, '', false);
}
$table->set_header($column++, get_lang('Groups'));

@ -0,0 +1,30 @@
<?php
/* For licensing terms, see /license.txt */
/**
* Class HookPortfolioItemAdded.
*/
class HookPortfolioItemAdded extends HookEvent implements HookPortfolioItemAddedEventInterface
{
/**
* HookPortfolioItemAdded constructor.
*
* @throws \Exception
*/
public function __construct()
{
parent::__construct('HookPortfolioItemAdded');
}
/**
* {@inheritDoc}
*/
public function notifyItemAdded()
{
/** @var \HookPortfolioItemAddedObserverInterface $observer */
foreach ($this->observers as $observer) {
$observer->hookItemAdded($this);
}
}
}

@ -0,0 +1,27 @@
<?php
/* For licensing terms, see /license.txt */
class HookPortfolioItemCommented extends HookEvent implements HookPortfolioItemCommentedEventInterface
{
/**
* HookPortfolioItemCommented constructor.
*
* @throws \Exception
*/
protected function __construct()
{
parent::__construct('HookPortfolioItemCommented');
}
/**
* {@inheritDoc}
*/
public function notifyItemCommented()
{
/** @var \HookPortfolioItemCommentedObserverInterface $observer */
foreach ($this->observers as $observer) {
$observer->hookItemCommented($this);
}
}
}

@ -0,0 +1,14 @@
<?php
/* For licensing terms, see /license.txt */
/**
* Interface HookPortfolioItemAddedEventInterface.
*/
interface HookPortfolioItemAddedEventInterface extends HookEventInterface
{
/**
* @return void
*/
public function notifyItemAdded();
}

@ -0,0 +1,16 @@
<?php
/* For licensing terms, see /license.txt */
/**
* Interface HookPortfolioItemAddedObserverInterface.
*/
interface HookPortfolioItemAddedObserverInterface extends HookObserverInterface
{
/**
* @param \HookPortfolioItemAddedEventInterface $hookEvent
*
* @return mixed
*/
public function hookItemAdded(HookPortfolioItemAddedEventInterface $hookEvent);
}

@ -0,0 +1,14 @@
<?php
/* For licensing terms, see /license.txt */
/**
* Interface HookPortfolioItemCommentedEventInterface.
*/
interface HookPortfolioItemCommentedEventInterface extends HookEventInterface
{
/**
* @return void
*/
public function notifyItemCommented();
}

@ -0,0 +1,16 @@
<?php
/* For licensing terms, see /license.txt */
/**
* Interface HookPortfolioItemCommentedObserverInterface.
*/
interface HookPortfolioItemCommentedObserverInterface extends HookObserverInterface
{
/**
* @param \HookPortfolioItemCommentedEventInterface $hookEvent
*
* @return mixed
*/
public function hookItemCommented(HookPortfolioItemCommentedEventInterface $hookEvent);
}

@ -7,7 +7,9 @@
interface HookQuizEndObserverInterface
{
/**
* @param \HookQuizEndEventInterface $hookEvent
*
* @return mixed
*/
public function hookQuizEnd(HookQuizEndEventInterface $hookvent);
public function hookQuizEnd(HookQuizEndEventInterface $hookEvent);
}

@ -15,11 +15,11 @@ CKEDITOR.plugins.setLang('oembed', 'fr', {
maxHeight: "Max. Hauteur:",
maxWidthTitle: "Largeur maximale du conteneur.",
maxHeightTitle: "Hauteur maximale du conteneur.",
resizeType: "Resize Type (Only Video's):",
noresize: "No Resize (use default)",
responsive: "Responsive Resize",
custom: "Specific Resize",
autoClose: "Automatically Close Dialog after Code is Embeded",
noVimeo: "The owner of this video has set domain restrictions and you will not be able to embed it on your website.",
Error: "Media Content could not been retrieved, please try a different URL."
resizeType: "Type de redimensionnement (uniquement les vidéos):",
noresize: "Pas de redimensionnement (par défaut)",
responsive: "Redimensionnement réactif",
custom: "Redimensionnement spécifique",
autoClose: "Fermer automatiquement la boîte de dialogue une fois le code incorporé",
noVimeo: "Le propriétaire de cette vidéo a défini des restrictions de domaine et vous ne pourrez pas l'intégrer à votre site Web.",
Error: "Impossible de récupérer le contenu multimédia, veuillez essayer une autre URL."
});

@ -1,8 +1,6 @@
/* For licensing terms, see /license.txt */
window.RecordAudio = (function () {
var isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
function startTimer() {
$("#timer").show();
var timerData = {
@ -126,8 +124,10 @@ window.RecordAudio = (function () {
stopTimer();
startTimer();
recordRTC = RecordRTC(stream, {
recorderType: isSafari ? RecordRTC.StereoAudioRecorder : RecordRTC.MediaStreamRecorder,
type: 'audio'
recorderType: RecordRTC.StereoAudioRecorder,
type: 'audio',
mimeType: 'audio/wav',
numberOfAudioChannels: 2
});
recordRTC.startRecording();
@ -185,7 +185,6 @@ window.RecordAudio = (function () {
if (!recordRTC) {
return;
}
stopTimer();
recordRTC.stopRecording(function (audioURL) {
btnStart.prop('disabled', false).removeClass('hidden');

@ -186,12 +186,15 @@ class PDF
* 1 => array('title'=>'Bye','path'=>'file2.html')
* );
* @param string $pdf_name pdf name
* @param string $course_code (if you are using html that are located
* @param null $course_code (if you are using html that are located
* in the document tool you must provide this)
* @param bool $print_title add title
* @param bool $complete_style show header and footer if true
* @param bool $addStyle
* @param string $mainTitle
* @param bool $generateToFile Optional. When it is TRUE, then the output file is move to app/cache
*
* @throws \MpdfException
*
* @return false|null
*/
@ -202,7 +205,8 @@ class PDF
$print_title = false,
$complete_style = true,
$addStyle = true,
$mainTitle = ''
$mainTitle = '',
$generateToFile = false
) {
if (empty($html_file_array)) {
return false;
@ -365,7 +369,15 @@ class PDF
$output_file = $pdf_name.'.pdf';
}
// F to save the pdf in a file
if ($generateToFile) {
@$this->pdf->Output(
api_get_path(SYS_ARCHIVE_PATH).$output_file,
'F'
);
} else {
@$this->pdf->Output($output_file, 'D');
}
exit;
}
@ -490,6 +502,7 @@ class PDF
$document_html
);
$document_html = str_replace(api_get_path(WEB_UPLOAD_PATH), api_get_path(SYS_UPLOAD_PATH), $document_html);
$document_html = str_replace(api_get_path(WEB_ARCHIVE_PATH), api_get_path(SYS_ARCHIVE_PATH), $document_html);
// The library mPDF expects UTF-8 encoded input data.

@ -58,6 +58,8 @@ class Security
return false;
}
// Clean $abs_path.
$abs_path = str_replace(['//', '../', './'], ['/', '', ''], $abs_path);
$true_path = str_replace("\\", '/', realpath($abs_path));
$checker_path = str_replace("\\", '/', realpath($checker_path));

@ -149,6 +149,7 @@ class Template
'api_get_user_info',
'api_get_configuration_value',
'api_get_setting',
'api_get_course_setting',
'api_get_plugin_setting',
[
'name' => 'return_message',
@ -446,9 +447,10 @@ class Template
// Only if course is available
$courseToolBar = '';
$origin = api_get_origin();
$show_course_navigation_menu = '';
if (!empty($this->course_id) && $this->user_is_logged_in) {
if (api_get_setting('show_toolshortcuts') != 'false') {
if ($origin !== 'embeddable' && api_get_setting('show_toolshortcuts') !== 'false') {
// Course toolbar
$courseToolBar = CourseHome::show_navigation_tool_shortcuts();
}

@ -3575,6 +3575,7 @@ class UserManager
$categoryStart = $row['session_category_date_start'] ? $row['session_category_date_start']->format('Y-m-d') : '';
$categoryEnd = $row['session_category_date_end'] ? $row['session_category_date_end']->format('Y-m-d') : '';
$courseList = self::get_courses_list_by_session($user_id, $session_id);
$daysLeft = SessionManager::getDayLeftInSession($row, $user_id);
// User portal filters:
@ -4077,9 +4078,19 @@ class UserManager
$checkPosition = array_filter(array_column($myCourseList, 'position'));
if (empty($checkPosition)) {
// The session course list doesn't have any position,
// then order the course list by course code
$list = array_column($myCourseList, 'course_code');
array_multisort($myCourseList, SORT_ASC, $list);
// then order the course list by course code.
$orderByCode = array_column($myCourseList, 'course_code');
sort($orderByCode, SORT_NATURAL);
$newCourseList = [];
foreach ($orderByCode as $code) {
foreach ($myCourseList as $course) {
if ($code === $course['course_code']) {
$newCourseList[] = $course;
break;
}
}
}
$myCourseList = $newCourseList;
}
}

@ -302,6 +302,8 @@ $_configuration['system_stable'] = NEW_VERSION_STABLE;
// Allows to do a remove_XSS in course introduction with user status COURSEMANAGERLOWSECURITY
// in order to accept all embed type videos (like vimeo, wistia, etc)
// $_configuration['course_introduction_html_strict_filtering'] = true;
// Allows to do a remove_XSS in question of exersice with user status COURSEMANAGER
// $_configuration['question_exercise_html_strict_filtering'] = true;
// Prevents the duplicate upload in assignments
// $_configuration['assignment_prevent_duplicate_upload'] = false;
//Show student progress in My courses page
@ -920,13 +922,15 @@ ALTER TABLE portfolio ADD CONSTRAINT FK_A9ED1062613FECDF FOREIGN KEY (session_id
ALTER TABLE portfolio ADD CONSTRAINT FK_A9ED106212469DE2 FOREIGN KEY (category_id) REFERENCES portfolio_category (id) ON DELETE SET NULL;
ALTER TABLE portfolio_category ADD CONSTRAINT FK_7AC64359A76ED395 FOREIGN KEY (user_id) REFERENCES user (id);
INSERT INTO settings_current(variable, subkey, type, category, selected_value, title, comment, scope, subkeytext, access_url_changeable) VALUES('course_create_active_tools','portfolio','checkbox','Tools','true','CourseCreateActiveToolsTitle','CourseCreateActiveToolsComment',NULL,'Portfolio', 0);
CREATE TABLE portfolio_comment (id INT AUTO_INCREMENT NOT NULL, author_id INT NOT NULL, item_id INT NOT NULL, tree_root INT DEFAULT NULL, parent_id INT DEFAULT NULL, content LONGTEXT NOT NULL, date DATETIME NOT NULL, is_important TINYINT(1) DEFAULT '0' NOT NULL, lft INT NOT NULL, lvl INT NOT NULL, rgt INT NOT NULL, INDEX IDX_C2C17DA2F675F31B (author_id), INDEX IDX_C2C17DA2126F525E (item_id), INDEX IDX_C2C17DA2A977936C (tree_root), INDEX IDX_C2C17DA2727ACA70 (parent_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE `utf8_unicode_ci` ENGINE = InnoDB;
CREATE TABLE portfolio_comment (id INT AUTO_INCREMENT NOT NULL, author_id INT NOT NULL, item_id INT NOT NULL, tree_root INT DEFAULT NULL, parent_id INT DEFAULT NULL, content LONGTEXT NOT NULL, date DATETIME NOT NULL, is_important TINYINT(1) DEFAULT '0' NOT NULL, lft INT NOT NULL, lvl INT NOT NULL, rgt INT NOT NULL, score DOUBLE PRECISION DEFAULT NULL, INDEX IDX_C2C17DA2F675F31B (author_id), INDEX IDX_C2C17DA2126F525E (item_id), INDEX IDX_C2C17DA2A977936C (tree_root), INDEX IDX_C2C17DA2727ACA70 (parent_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE `utf8_unicode_ci` ENGINE = InnoDB;
ALTER TABLE portfolio_comment ADD CONSTRAINT FK_C2C17DA2F675F31B FOREIGN KEY (author_id) REFERENCES user (id);
ALTER TABLE portfolio_comment ADD CONSTRAINT FK_C2C17DA2126F525E FOREIGN KEY (item_id) REFERENCES portfolio (id);
ALTER TABLE portfolio_comment ADD CONSTRAINT FK_C2C17DA2A977936C FOREIGN KEY (tree_root) REFERENCES portfolio_comment (id) ON DELETE CASCADE;
ALTER TABLE portfolio_comment ADD CONSTRAINT FK_C2C17DA2727ACA70 FOREIGN KEY (parent_id) REFERENCES portfolio_comment (id) ON DELETE CASCADE;
ALTER TABLE portfolio ADD origin INT DEFAULT NULL, ADD origin_type INT DEFAULT NULL;
ALTER TABLE portfolio ADD score DOUBLE PRECISION DEFAULT NULL;
INSERT INTO extra_field (extra_field_type, field_type, variable, display_text, visible_to_self, visible_to_others, changeable, created_at) VALUES (19, 10, 'tags', 'tags', 1, 1, 1, NOW());
CREATE TABLE portfolio_attachment (id INT AUTO_INCREMENT NOT NULL, path VARCHAR(255) NOT NULL, comment LONGTEXT DEFAULT NULL, size INT NOT NULL, filename VARCHAR(255) NOT NULL, origin_id INT NOT NULL, origin_type INT NOT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE `utf8_unicode_ci` ENGINE = InnoDB;
*/
// In 1.11.8, before enabling this feature, you also need to:
// - edit src/Chamilo/CoreBundle/Entity/Portfolio.php and PortfolioCategory.php
@ -990,6 +994,8 @@ VALUES (2, 13, 'session_courses_read_only_mode', 'Lock Course In Session', 1, 1,
// Set to true to disable the new personal data page inside the social network
// menu
// $_configuration['disable_gdpr'] = true;
// Set the LinkedIn organization id BT#17468
//$_configuration['linkedin_organization_id'] = false;
// GDPR requires users to be informed of the Data Protection Officer name and
// contact point. These can only be defined here for now, but will be moved to
@ -1820,6 +1826,24 @@ ALTER TABLE gradebook_comment ADD CONSTRAINT FK_C3B70763AD3ED51C FOREIGN KEY (gr
// Add certificate footer. Add your template main/template/default/export/pdf_certificate_footer.tpl
// $_configuration['add_certificate_pdf_footer'] = true;
// Shows a popup with the list of answered/unanswered questions before sending a test.
// $_configuration['quiz_check_all_answers_before_end_test'] = true;
// Custom cloud link URLS, this requires enable_add_file_link = true
// $_configuration['documents_custom_cloud_link_list'] = ['links' => ['example.com', 'example2.com']];
// Shows exercise session attempts in the base course.
// $_configuration['show_exercise_session_attempts_in_base_course'] = false;
// Allow coach users to always edit announcements inside active/past sessions.
// $_configuration['allow_coach_to_edit_announcements'] = false;
// Show invisible LP in the course home for students. BT#17744
//$_configuration['show_invisible_lp_in_course_home'] = true;
// Show start/end date in LP list for students.
//$_configuration['lp_start_and_end_date_visible_in_student_view'] = true;
// KEEP THIS AT THE END
// -------- Custom DB changes
// Add user activation by confirmation email

@ -615,7 +615,7 @@ $Stats = "Statistics";
$UplPage = "Upload page and link to Home Page";
$LinkSite = "Add link to page on Home Page";
$HasDel = "has been deleted";
$ByDel = "Deleting this area will permanently delete all the content (documents, links...) it contains and unregister all its members (not remove them from other courses). <p>Do you really want to delete the course?";
$ByDel = "Deleting this area will permanently delete all the content (documents, links...) it contains and unregister all its members (not remove them from other courses).";
$Y = "YES";
$N = "NO";
$DepartmentUrl = "Department URL";
@ -6675,7 +6675,7 @@ $NewExercises = "New exercises";
$MyAverage = "My average";
$AllAttempts = "All attempts";
$QuestionsToReview = "Questions to be reviewed";
$QuestionWithNoAnswer = "Questions without answer";
$QuestionWithNoAnswer = "Questions without answer will be highlighted in Red below";
$ValidateAnswers = "Validate answers";
$ReviewQuestions = "Review selected questions";
$YouTriedToResolveThisExerciseEarlier = "You have tried to resolve this exercise earlier";
@ -7574,7 +7574,7 @@ $Convert = "Convert";
$PortalLimitType = "Portal's limit type";
$PortalName = "Portal name";
$BestScore = "Best score";
$AreYouSureToDeleteJS = "Are you sure to delete";
$AreYouSureToDeleteJS = "Are you sure to delete?";
$ConversionToSameFileFormat = "Conversion to same file format. Please choose another.";
$FileFormatNotSupported = "File format not supported";
$FileConvertedFromXToY = "File converted from %s to %s";
@ -8720,4 +8720,45 @@ $ShowScoreEveryAttemptShowAnswersLastAttemptNoFeedback = "Show the result to the
$AddGradebookComment = "Comment";
$LatestLoginInAnyCourse = "Latest login in a course";
$The = "The";
$MultiplicateQuestionsByClass = "Multiplicate questions by class";
$MultiplicateQuestionsByUser = "Multiplicate questions by user";
$QuestionForNextUser = "Questions about next user";
$CourseCodeToEnteredCapitalLettersToConfirmDeletionX = "Course code to be entered in capital letters to confirm the deletion: %s";
$BadgeXTitle = "Badge: %s";
$PortfolioCommentFromXUser = "Comment by %s";
$PortfolioItemFromXUser = "Portfolio item by %s";
$CopyToMyPortfolio = "Copy to my portfolio";
$CopyToStudentPortfolio = "Copy to student portfolio";
$OriginallyPublishedAsXTitleByYUser = "Originally published as \"%s\" by %s";
$OriginallyCommentedByXUserInYItem = "Originally commented by %s in \"%s\"";
$PortfolioItemAddedToStudents = "Item added to students own portfolio";
$MarkCommentAsImportant = "Mark comment as important";
$UnmarkCommentAsImportant = "Unmark comment as important";
$CommentMarkedAsImportant = "Portfolio item marked as important";
$SelectLearnerPortfolio = "Select a learner portfolio";
$SeeMyPortfolio = "See my portfolio in this course";
$PortfolioDetails = "Portfolio details";
$PortfolioItemTitle = "Item title";
$CreationDateXDate = "Creation date: %s";
$UpdateDateXDate = "Update date: %s";
$CategoryXName = "Category: %s";
$DateXDate = "Date: %s";
$PortfolioItemTitleXName = "Item title: %s";
$NoItemsInYourPortfolio = "No items in your portfolio";
$YouHaveNotCommented = "You have not commented";
$PortfolioItems = "Portfolio items";
$PortfolioComments = "Portfolio comments";
$PortfolioCommentsMade = "Comments made";
$QualifyThisPortfolioItem = "Grade this item";
$PortfolioItemGraded = "Portfolio item was graded";
$ContextForCommentToBeGrade = "Context for the comment to be grade";
$QualifyThisPortfolioComment = "Grade this comment";
$PortfolioCommentGraded = "Portfolio comment was graded";
$QualifyPortfolioItems = "Grade items";
$QualifyPortfolioComments = "Grade comments";
$HideCorrectAnsweredQuestions = "Hide correct answered questions";
$TheSettingXWillChangeToX = "The setting \"%s\" will change to \"%s\"";
$VerificationOfAnsweredQuestions = "Verification of answered questions";
$StudentPublicationToCorrect = "Student's assignments to be corrected";
$StudentPublicationCorrectionWarning = "You will find below all the work that have been submitted by students in one of your course (it could be in the base course or in a course in a session). You can filter the list selecting a specific course or a work status.";
?>

@ -607,7 +607,7 @@ $Stats = "Suivi";
$UplPage = "Déposer page et lier à l'accueil";
$LinkSite = "Ajouter un lien sur la page d'accueil";
$HasDel = "a été supprimé";
$ByDel = "En supprimant ce cours, vous supprimerez tous les documents qu'il contient et désinscrirez tous les membres qui y sont inscrits. <p>Voulez-vous réellement supprimer ce cours ?";
$ByDel = "En supprimant ce cours, vous supprimerez tous les documents qu'il contient et désinscrirez tous les membres qui y sont inscrits.";
$Y = "OUI";
$N = "NON";
$DepartmentUrl = "URL du département";
@ -6662,7 +6662,7 @@ $NewExercises = "Nouveaux exercices";
$MyAverage = "Ma moyenne";
$AllAttempts = "Toutes les tentatives";
$QuestionsToReview = "Questions à revoir";
$QuestionWithNoAnswer = "Questions sans réponses";
$QuestionWithNoAnswer = "Questions sans réponses surlignées en rouge ci-dessous";
$ValidateAnswers = "Finaliser l'exercice";
$ReviewQuestions = "Revoir les questions sélectionnées";
$YouTriedToResolveThisExerciseEarlier = "Vous avez essayé de résoudre cet exercice plus tôt";
@ -6929,7 +6929,7 @@ $DatabaseXWillBeCreated = "La base de donnée %s va être créée";
$ADatabaseWithTheSameNameAlreadyExists = "Une base de donnée du même nom existe déjà. Si vous utilisez cette base, son contenu actuel sera écrasé.";
$UserXCantHaveAccessInTheDatabaseX = "L'utilisateur %s n'a pas accès à la base de donnée %s";
$DatabaseXCantBeCreatedUserXDoestHaveEnoughPermissions = "La base de donnée %s ne peut pas être crée, l'utilisateur %s n'a pas suffisamment de permissions.";
$CopyOnlySessionItems = "Copier seulement les élémenents de la session";
$CopyOnlySessionItems = "Copier seulement les éléments de la session";
$FirstLetterCourseTitle = "Première lettre (title)";
$NumberOfPublishedExercises = "# d'exerices publiés";
$NumberOfPublishedLps = "# de parcours publiés";
@ -7524,7 +7524,7 @@ $Convert = "Convertir";
$PortalLimitType = "Type de limite du portail";
$PortalName = "Nom du portail";
$BestScore = "Meilleur score";
$AreYouSureToDeleteJS = "Êtes-vous certain de vouloir supprimer";
$AreYouSureToDeleteJS = "Êtes-vous certain de vouloir supprimer ?";
$ConversionToSameFileFormat = "Conversion au format original bloquée. Veuillez choisir un autre format.";
$FileFormatNotSupported = "Format de fichier non supporté.";
$FileConvertedFromXToY = "Fichier converti de %s à %s";
@ -8652,4 +8652,45 @@ $ShowScoreEveryAttemptShowAnswersLastAttemptNoFeedback = "Montrer le score, le c
$AddGradebookComment = "Commentaire";
$LatestLoginInAnyCourse = "Dernier accès à un cours";
$The = "Le";
$MultiplicateQuestionsByClass = "Multiplier les questions par classe";
$MultiplicateQuestionsByUser = "Multiplier les questions par utilisateur";
$QuestionForNextUser = "Questions concernant l'apprenant suivant";
$CourseCodeToEnteredCapitalLettersToConfirmDeletionX = "Code du cours à saisir en majuscules pour confirmer la suppression : %s";
$BadgeXTitle = "Badge : %s";
$PortfolioCommentFromXUser = "Commenté par %s";
$PortfolioItemFromXUser = "Item de Portfolio de %s";
$CopyToMyPortfolio = "Copier dans mon portfolio";
$CopyToStudentPortfolio = "Copier dans le portfolio de l'étudiant";
$OriginallyPublishedAsXTitleByYUser = "Publier à l'origine comme %s par %s";
$OriginallyCommentedByXUserInYItem = "Commenté à l'origine par %s dans %s";
$PortfolioItemAddedToStudents = "Item rajouter dans le portfolio de l'apprenant";
$MarkCommentAsImportant = "Marquer le commentaire comme important";
$UnmarkCommentAsImportant = "Enlever la marque d'importance sur le commentaire";
$CommentMarkedAsImportant = "Item de portfolio marqué comme important";
$SelectLearnerPortfolio = "Choisir le portfolio d'un apprenant";
$SeeMyPortfolio = "Voir mon portfolio dans ce cours";
$PortfolioDetails = "Détails du portfolio";
$PortfolioItemTitle = "Titre de l'item";
$CreationDateXDate = "Date de création : %s";
$UpdateDateXDate = "Date de mise à jour : %s";
$CategoryXName = "Catégorie : %s";
$DateXDate = "Date : %s";
$PortfolioItemTitleXName = "Titre de l'item : %s";
$NoItemsInYourPortfolio = "Aucun item dans votre portfolio";
$YouHaveNotCommented = "Vous n'avez pas commenté";
$PortfolioItems = "Items de portfolio";
$PortfolioComments = "Commentaires de portfolio";
$PortfolioCommentsMade = "Commentaires réalisés";
$QualifyThisPortfolioItem = "Noter cet item";
$PortfolioItemGraded = "L'item de portfolio a été noté";
$ContextForCommentToBeGrade = "Context pour le commentaire à noter";
$QualifyThisPortfolioComment = "Noter ce commentaire";
$PortfolioCommentGraded = "Le commentaire du portfolio a été noté";
$QualifyPortfolioItems = "Noter les items";
$QualifyPortfolioComments = "Évaluer les commentaires";
$HideCorrectAnsweredQuestions = "Cacher les questions qui ont eu une réponse correcte";
$TheSettingXWillChangeToX = "Le paramètre \"%s\" sera modifié avec \"%s\"";
$VerificationOfAnsweredQuestions = "Vérification des réponses aux questions";
$StudentPublicationToCorrect = "Travaux d'étudiant à corriger";
$StudentPublicationCorrectionWarning = "Vous trouverez ci-dessous tous les travaux qui ont été soumis par des étudiants dans l'un de vos cours (cela peut être dans le cours de base ou dans un cours en session). Vous pouvez filtrer la liste en sélectionnant un cours spécifique ou un statut de travail.";
?>

@ -619,7 +619,7 @@ $Stats = "Estadísticas";
$UplPage = "Enviar una página y enlazarla a la principal";
$LinkSite = "Añadir un enlace web en la página principal";
$HasDel = "ha sido suprimido";
$ByDel = "Si suprime el sitio web de este curso, suprimirá todos los documentos que contiene y todos sus miembros dejarán de estar inscritos en el mismo. <p>¿ Está seguro de querer suprimir este curso ?";
$ByDel = "Si suprime el sitio web de este curso, suprimirá todos los documentos que contiene y todos sus miembros dejarán de estar inscritos en el mismo.";
$Y = "SI";
$N = "NO";
$DepartmentUrl = "URL del departamento";
@ -6679,7 +6679,7 @@ $NewExercises = "Nuevo ejercicio";
$MyAverage = "Mi promedio";
$AllAttempts = "Todos los intentos";
$QuestionsToReview = "Preguntas que desea comprobar";
$QuestionWithNoAnswer = "Preguntas sin responder";
$QuestionWithNoAnswer = "Preguntas sin respuesta resaltadas en rojo a continuación";
$ValidateAnswers = "Validar respuestas";
$ReviewQuestions = "Revisar las preguntas seleccionadas";
$YouTriedToResolveThisExerciseEarlier = "Ya intentó resolver este ejercicio anteriormente";
@ -7607,7 +7607,7 @@ $Convert = "Convertir";
$PortalLimitType = "Tipo de límite del portal";
$PortalName = "Nombre del portal";
$BestScore = "Mejor calificación";
$AreYouSureToDeleteJS = "Está seguro de eliminar";
$AreYouSureToDeleteJS = "¿Está seguro de eliminar?";
$ConversionToSameFileFormat = "Conversión al mismo formato de archivo. Por favor, selecciones otro.";
$FileFormatNotSupported = "Formato de archivo no soportado.";
$FileConvertedFromXToY = "Archivo convertido de %s a %s";
@ -8748,4 +8748,45 @@ $ShowScoreEveryAttemptShowAnswersLastAttemptNoFeedback = "Mostrar el resultado a
$AddGradebookComment = "Comentario";
$LatestLoginInAnyCourse = "Último acceso en un curso";
$The = "El";
$MultiplicateQuestionsByClass = "Multiplicar las preguntas por clase";
$MultiplicateQuestionsByUser = "Multiplicar las preguntas por usuario";
$QuestionForNextUser = "Preguntas sobre el usuario siguiente";
$CourseCodeToEnteredCapitalLettersToConfirmDeletionX = "El código del curso que debe ingresar en mayúsculas para confirmar la eliminación: %s";
$BadgeXTitle = "Insignia: %s";
$PortfolioCommentFromXUser = "Comentario de %s";
$PortfolioItemFromXUser = "Artículo del portafolio de %s";
$CopyToMyPortfolio = "Copiar a mi portafolio";
$CopyToStudentPortfolio = "Copiar al portafolio del estudiante";
$OriginallyPublishedAsXTitleByYUser = "Originalmente publicado como \"%s\" por %s";
$OriginallyCommentedByXUserInYItem = "Originalmente comentado por %s en \"%s\"";
$PortfolioItemAddedToStudents = "Artículo agregado al portafolio de los estudiantes";
$MarkCommentAsImportant = "Marcar comentario como importante";
$UnmarkCommentAsImportant = "Desmarcar comentario como importante";
$CommentMarkedAsImportant = "Cometario marcado como importante";
$SelectLearnerPortfolio = "Seleccionar un portafolio de estudiante";
$SeeMyPortfolio = "Ver mi portafolio en este curso";
$PortfolioDetails = "Detalles del portafolio";
$PortfolioItemTitle = "Título del artículo";
$CreationDateXDate = "Fecha de creación: %s";
$UpdateDateXDate = "Fecha de actualización: %s";
$CategoryXName = "Categoría: %s";
$DateXDate = "Fecha: %s";
$PortfolioItemTitleXName = "Título del artículo: %s";
$NoItemsInYourPortfolio = "No hay artículos en su portafolio";
$YouHaveNotCommented = "Usted no ha comentado";
$PortfolioItems = "Artículos del portafolio";
$PortfolioComments = "Comentarios en portafolio";
$PortfolioCommentsMade = "Comentarios hechos";
$QualifyThisPortfolioItem = "Calificar este artículo";
$PortfolioItemGraded = "El artículo del portafolio fue calificado";
$ContextForCommentToBeGrade = "Contexto para el comentario a ser calificado";
$QualifyThisPortfolioComment = "Calificar este comentario";
$PortfolioCommentGraded = "El comentario en el portafolio fue calificado";
$QualifyPortfolioItems = "Calificar artículos";
$QualifyPortfolioComments = "Calificar comentarios";
$HideCorrectAnsweredQuestions = "Esconder las preguntas con respuestas correctas";
$TheSettingXWillChangeToX = "La opción \"%s\" será modificada con el valor \"%s\"";
$VerificationOfAnsweredQuestions = "Verificación de preguntas respondidas";
$StudentPublicationToCorrect = "Tareas de estudiante para ser corregidas";
$StudentPublicationCorrectionWarning = "A continuación encontrará todos los trabajos que han enviado los alumnos en uno de sus cursos (puede ser en el curso base o en un curso en una sesión). Puede filtrar la lista seleccionando un curso específico o un estado de tarea.";
?>

@ -2343,7 +2343,6 @@ class learnpath
$now = time();
if (Database::num_rows($rs) > 0) {
$row = Database::fetch_array($rs, 'ASSOC');
if (!empty($row['category_id'])) {
$category = self::getCategory($row['category_id']);
if (self::categoryIsVisibleForStudent($category, api_get_user_entity($student_id)) === false) {
@ -2548,7 +2547,7 @@ class learnpath
*/
public function getProgressBar($mode = null)
{
list($percentage, $text_add) = $this->get_progress_bar_text($mode);
[$percentage, $text_add] = $this->get_progress_bar_text($mode);
return self::get_progress_bar($percentage, $text_add);
}
@ -3577,7 +3576,7 @@ class learnpath
$lp_item_params = $row['liparams'];
if (empty($lp_item_params) && strpos($lp_item_path, '?') !== false) {
list($lp_item_path, $lp_item_params) = explode('?', $lp_item_path);
[$lp_item_path, $lp_item_params] = explode('?', $lp_item_path);
}
$sys_course_path = api_get_path(SYS_COURSE_PATH).api_get_course_path();
if ($type === 'http') {
@ -3640,6 +3639,7 @@ class learnpath
$file = 'lp_content.php?type=dir&'.api_get_cidreq();
break;
case 'link':
if (!empty($file)) {
if (Link::is_youtube_link($file)) {
$src = Link::get_youtube_video_id($file);
$file = api_get_path(WEB_CODE_PATH).'lp/embed.php?type=youtube&source='.$src;
@ -3656,7 +3656,10 @@ class learnpath
$linkProtocol = substr($file, 0, 5);
if ($linkProtocol === 'http:') {
//this is the special intervention case
$file = api_get_path(WEB_CODE_PATH).'lp/embed.php?type=nonhttps&source='.urlencode($file);
$file = api_get_path(
WEB_CODE_PATH
).'lp/embed.php?type=nonhttps&source='.urlencode($file);
}
}
}
}
@ -3755,7 +3758,7 @@ class learnpath
if (!is_file(realpath($sys_course_path.'/scorm/'.$lp_path.'/'.$lp_item_path))) {
// if file not found.
$decoded = html_entity_decode($lp_item_path);
list($decoded) = explode('?', $decoded);
[$decoded] = explode('?', $decoded);
if (!is_file(realpath($sys_course_path.'/scorm/'.$lp_path.'/'.$decoded))) {
$file = self::rl_get_resource_link_for_learnpath(
$course_id,
@ -4394,6 +4397,10 @@ class learnpath
*/
public static function toggle_visibility($lp_id, $set_visibility = 1)
{
if (empty($lp_id)) {
return false;
}
$action = 'visible';
if ($set_visibility != 1) {
$action = 'invisible';
@ -4416,11 +4423,6 @@ class learnpath
* @param int $id
* @param int $visibility
*
* @throws \Doctrine\ORM\NonUniqueResultException
* @throws \Doctrine\ORM\ORMException
* @throws \Doctrine\ORM\OptimisticLockException
* @throws \Doctrine\ORM\TransactionRequiredException
*
* @return bool
*/
public static function toggleCategoryVisibility($id, $visibility = 1)
@ -4452,6 +4454,9 @@ class learnpath
*/
public static function toggle_publish($lp_id, $set_visibility = 'v')
{
if (empty($lp_id)) {
return false;
}
$course_id = api_get_course_int_id();
$tbl_lp = Database::get_course_table(TABLE_LP_MAIN);
$lp_id = (int) $lp_id;
@ -4461,10 +4466,10 @@ class learnpath
if (Database::num_rows($result)) {
$row = Database::fetch_array($result);
$name = Database::escape_string($row['name']);
if ($set_visibility == 'i') {
if ($set_visibility === 'i') {
$v = 0;
}
if ($set_visibility == 'v') {
if ($set_visibility === 'v') {
$v = 1;
}
@ -4475,21 +4480,32 @@ class learnpath
$link = 'lp/lp_controller.php?action=view&lp_id='.$lp_id.'&id_session='.$session_id;
$oldLink = 'newscorm/lp_controller.php?action=view&lp_id='.$lp_id.'&id_session='.$session_id;
$extraLpCondition = '';
$extraLink = '';
if (!empty($session_id)) {
$extraLink = 'lp/lp_controller.php?action=view&lp_id='.$lp_id.'&id_session=0';
$extraLpCondition = " OR (link = '$link' AND session_id = $session_id ) ";
}
$sql = "SELECT * FROM $tbl_tool
WHERE
c_id = $course_id AND
(link = '$link' OR link = '$oldLink') AND
(link = '$link' OR link = '$oldLink' $extraLpCondition ) AND
image = 'scormbuilder.gif' AND
(
link LIKE '$link%' OR
link LIKE '$oldLink%'
$extraLpCondition
)
$session_condition
";
$result = Database::query($sql);
$num = Database::num_rows($result);
if ($set_visibility == 'i' && $num > 0) {
$resultTool = Database::fetch_array($result, 'ASSOC');
if ($set_visibility === 'i') {
if ($num > 0) {
$sql = "DELETE FROM $tbl_tool
WHERE
c_id = $course_id AND
@ -4497,16 +4513,63 @@ class learnpath
image='scormbuilder.gif'
$session_condition";
Database::query($sql);
} elseif ($set_visibility == 'v' && $num == 0) {
$sql = "INSERT INTO $tbl_tool (category, c_id, name, link, image, visibility, admin, address, added_tool, session_id) VALUES
('authoring', $course_id, '$name', '$link', 'scormbuilder.gif', '$v', '0','pastillegris.gif', 0, $session_id)";
}
// Disables the base course link inside a session.
if (!empty($session_id) && 0 === (int) $row['session_id']) {
$sql = "SELECT iid FROM $tbl_tool
WHERE
c_id = $course_id AND
(link = '$extraLink') AND
image = 'scormbuilder.gif' AND
session_id = $session_id
";
$resultBaseLp = Database::query($sql);
if (Database::num_rows($resultBaseLp)) {
$resultBaseLpRow = Database::fetch_array($resultBaseLp);
$id = $resultBaseLpRow['iid'];
/*$sql = "UPDATE $tbl_tool
SET visibility = 0
WHERE iid = $id ";
Database::query($sql);*/
$sql = "DELETE FROM $tbl_tool
WHERE iid = $id";
Database::query($sql);
} else {
/*$params = [
'category' => 'authoring',
'c_id' => $course_id,
'name' => $name,
'link' => $link,
'image' => 'scormbuilder.gif',
'visibility' => '0',
'admin' => '0',
'address' => 'pastillegris.gif',
'added_tool' => '0',
'session_id' => $session_id,
];
$insertId = Database::insert($tbl_tool, $params);
if ($insertId) {
$sql = "UPDATE $tbl_tool SET id = iid WHERE iid = $insertId";
Database::query($sql);
}*/
}
}
}
if ($set_visibility === 'v') {
if ($num == 0) {
$sql = "INSERT INTO $tbl_tool (category, c_id, name, link, image, visibility, admin, address, added_tool, session_id)
VALUES ('authoring', $course_id, '$name', '$link', 'scormbuilder.gif', '$v', '0','pastillegris.gif', 0, $session_id)";
Database::query($sql);
$insertId = Database::insert_id();
if ($insertId) {
$sql = "UPDATE $tbl_tool SET id = iid WHERE iid = $insertId";
Database::query($sql);
}
} elseif ($set_visibility == 'v' && $num > 0) {
}
if ($num > 0) {
$id = $resultTool['iid'];
$sql = "UPDATE $tbl_tool SET
c_id = $course_id,
name = '$name',
@ -4519,13 +4582,12 @@ class learnpath
session_id = $session_id
WHERE
c_id = ".$course_id." AND
(link = '$link' OR link = '$oldLink') AND
image='scormbuilder.gif'
$session_condition
iid = $id
";
Database::query($sql);
}
}
}
return false;
}
@ -4537,11 +4599,6 @@ class learnpath
* @param int $id
* @param int $setVisibility
*
* @throws \Doctrine\ORM\NonUniqueResultException
* @throws \Doctrine\ORM\ORMException
* @throws \Doctrine\ORM\OptimisticLockException
* @throws \Doctrine\ORM\TransactionRequiredException
*
* @return bool
*/
public static function toggleCategoryPublish($id, $setVisibility = 1)
@ -4947,7 +5004,7 @@ class learnpath
if (!api_is_invitee()) {
// Save progress.
list($progress) = $this->get_progress_bar_text('%');
[$progress] = $this->get_progress_bar_text('%');
$scoreAsProgressSetting = api_get_configuration_value('lp_score_as_progress_enable');
$scoreAsProgress = $this->getUseScoreAsProgress();
if ($scoreAsProgress && $scoreAsProgressSetting && (null === $score || empty($score) || -1 == $score)) {
@ -12076,9 +12133,7 @@ EOD;
$cb->set_tools_to_build(['learnpaths']);
//Setting elements that will be copied
$cb->set_tools_specific_id_list(
['learnpaths' => [$this->lp_id]]
);
$cb->set_tools_specific_id_list(['learnpaths' => [$this->lp_id]]);
$course = $cb->build();
@ -12460,18 +12515,22 @@ EOD;
$cats = [get_lang('SelectACategory')];
}
$checkSession = false;
$sessionId = api_get_session_id();
$avoidCategoryInSession = false;
if (empty($sessionId)) {
$avoidCategoryInSession = true;
}
/*$checkSession = false;
if (api_get_configuration_value('allow_session_lp_category')) {
$checkSession = true;
}
}*/
if (!empty($items)) {
foreach ($items as $cat) {
$categoryId = $cat->getId();
if ($checkSession) {
if ($avoidCategoryInSession) {
$inSession = self::getCategorySessionId($categoryId);
if ($inSession != $sessionId) {
if (!empty($inSession)) {
continue;
}
}
@ -13259,10 +13318,10 @@ EOD;
return $main_dir_path.'exercise/overview.php?'.$extraParams.'&'
.http_build_query([
'lp_init' => 1,
'learnpath_item_view_id' => $learnpathItemViewId,
'learnpath_id' => $learningPathId,
'learnpath_item_id' => $id_in_path,
'exerciseId' => $id,
'learnpath_item_view_id' => (int) $learnpathItemViewId,
'learnpath_id' => (int) $learningPathId,
'learnpath_item_id' => (int) $id_in_path,
'exerciseId' => (int) $id,
]);
case TOOL_HOTPOTATOES: //lowercase because of strtolower above
$TBL_DOCUMENT = Database::get_course_table(TABLE_DOCUMENT);
@ -13320,13 +13379,23 @@ EOD;
Session::write('openmethod', $openmethod);
Session::write('officedoc', $officedoc);
// Same validation as in document/download.php
$isVisible = DocumentManager::is_visible(
$document->getPath(),
api_get_course_info(),
api_get_session_id()
);
if (!api_is_allowed_to_edit() && !$isVisible) {
return '';
}
if ($showDirectUrl) {
$file = $main_course_path.'document'.$document->getPath().'?'.$extraParams;
if (api_get_configuration_value('allow_pdf_viewerjs_in_lp')) {
if (Link::isPdfLink($file)) {
$pdfUrl = api_get_path(WEB_LIBRARY_PATH).'javascript/ViewerJS/index.html#'.$file;
return $pdfUrl;
return api_get_path(WEB_LIBRARY_PATH).
'javascript/ViewerJS/index.html?zoom=page-width#'.$file;
}
}

@ -104,27 +104,52 @@ class LearnpathList
$showBlockedPrerequisite = api_get_configuration_value('show_prerequisite_as_blocked');
$names = [];
$isAllowToEdit = api_is_allowed_to_edit();
$toolSessionCondition = api_get_session_condition($session_id);
/** @var CLp $row */
foreach ($learningPaths as $row) {
$name = Database::escape_string($row->getName());
$link = 'lp/lp_controller.php?action=view&lp_id='.$row->getId().'&id_session='.$session_id;
$oldLink = 'newscorm/lp_controller.php?action=view&lp_id='.$row->getId().'&id_session='.$session_id;
$extraCondition = '';
if (!empty($session_id)) {
$extraLink = 'lp/lp_controller.php?action=view&lp_id='.$row->getId().'&id_session=0';
$extraCondition = " OR link LIKE '$extraLink' ";
}
$sql2 = "SELECT visibility FROM $tbl_tool
WHERE
c_id = $course_id AND
name = '$name' AND
image = 'scormbuilder.gif' AND
(
link LIKE '$link%' OR
link LIKE '$oldLink%'
$extraCondition
)
$toolSessionCondition
";
$res2 = Database::query($sql2);
$pub = 'i';
if (Database::num_rows($res2) > 0) {
$row2 = Database::fetch_array($res2);
$pub = $row2['visibility'];
$pub = (int) $row2['visibility'];
if (!empty($session_id)) {
$pub = 'v';
// Check exact value in session:
/*$sql3 = "SELECT visibility FROM $tbl_tool
WHERE
c_id = $course_id AND
image = 'scormbuilder.gif' AND
( link LIKE '$link'
)
$toolSessionCondition
";
$res3 = Database::query($sql3);
if (Database::num_rows($res3)) {
$pub = 'v';
}*/
//$pub = 0 === $pub ? 'i' : 'v';
}
}
// Check if visible.

@ -36,6 +36,7 @@ function activate_end_date() {
$is_allowed_to_edit = api_is_allowed_to_edit(null, true);
$isStudentView = isset($_REQUEST['isStudentView']) ? $_REQUEST['isStudentView'] : null;
$learnpath_id = isset($_REQUEST['lp_id']) ? $_REQUEST['lp_id'] : null;
$sessionId = api_get_session_id();
if (!$is_allowed_to_edit || $isStudentView) {
header('location:lp_controller.php?action=view&lp_id='.$learnpath_id.'&'.api_get_cidreq());

@ -286,7 +286,6 @@ $htmlHeadXtra[] = '
</script>';
$session_id = api_get_session_id();
$lpfound = false;
$myrefresh = 0;
$myrefresh_id = 0;
@ -491,6 +490,8 @@ if ($debug > 0) {
error_log('action "'.$action.'" triggered');
}
$lpListUrl = api_get_self().'?action=list&'.api_get_cidreq();
switch ($action) {
case 'author_view':
$teachers = [];
@ -876,7 +877,7 @@ switch ($action) {
require 'lp_list.php';
} else {
Session::write('refresh', 1);
if (isset($_POST['submit_button']) && !empty($post_title)) {
if (isset($_POST) && !empty($post_title)) {
// Updating the lp.modified_on
$_SESSION['oLP']->set_modified_on();
@ -907,7 +908,6 @@ switch ($action) {
$_SESSION['oLP']->edit_document($_course);
}
$is_success = true;
$extraFieldValues = new ExtraFieldValue('lp_item');
$extraFieldValues->saveFieldValues($_POST, true);
@ -1102,21 +1102,18 @@ switch ($action) {
learnpath::toggleCategoryVisibility($_REQUEST['id'], $_REQUEST['new_status']);
Display::addFlash(Display::return_message(get_lang('Updated')));
header('Location: '.api_get_self().'?'.api_get_cidreq());
exit;
api_location($lpListUrl);
break;
case 'toggle_visible':
// Change lp visibility (inside lp tool).
if (!$is_allowed_to_edit) {
api_not_allowed(true);
}
if (!$lp_found) {
require 'lp_list.php';
} else {
learnpath::toggle_visibility($_REQUEST['lp_id'], $_REQUEST['new_status']);
Display::addFlash(Display::return_message(get_lang('Updated')));
require 'lp_list.php';
}
api_location($lpListUrl);
break;
case 'toggle_category_publish':
if (!$is_allowed_to_edit) {
@ -1125,20 +1122,18 @@ switch ($action) {
learnpath::toggleCategoryPublish($_REQUEST['id'], $_REQUEST['new_status']);
Display::addFlash(Display::return_message(get_lang('Updated')));
require 'lp_list.php';
api_location($lpListUrl);
break;
case 'toggle_publish':
// Change lp published status (visibility on homepage).
if (!$is_allowed_to_edit) {
api_not_allowed(true);
}
if (!$lp_found) {
require 'lp_list.php';
} else {
learnpath::toggle_publish($_REQUEST['lp_id'], $_REQUEST['new_status']);
Display::addFlash(Display::return_message(get_lang('Updated')));
require 'lp_list.php';
}
api_location($lpListUrl);
break;
case 'move_lp_up':
// Change lp published status (visibility on homepage)

@ -210,6 +210,7 @@ $courseSettingsDisableIcon = Display::return_icon(
$enableAutoLaunch = api_get_course_setting('enable_lp_auto_launch');
$gameMode = api_get_setting('gamification_mode');
$allowDatesForStudent = api_get_configuration_value('lp_start_and_end_date_visible_in_student_view');
$data = [];
$tableCategory = Database::get_course_table(TABLE_LP_CATEGORY);
@ -235,7 +236,6 @@ foreach ($categories as $item) {
continue;
}
}
if ($allowCategory && !empty($sessionId)) {
// Check base course
if (0 === $item->getSessionId()) {
@ -316,14 +316,14 @@ foreach ($categories as $item) {
}
$start_time = $end_time = '';
if ($is_allowed_to_edit) {
if (!empty($details['publicated_on'])) {
$start_time = api_convert_and_format_date($details['publicated_on'], DATE_TIME_FORMAT_LONG_24H);
}
if (!empty($details['expired_on'])) {
$end_time = api_convert_and_format_date($details['expired_on'], DATE_TIME_FORMAT_LONG_24H);
}
} else {
if (!$is_allowed_to_edit) {
$time_limits = false;
// This is an old LP (from a migration 1.8.7) so we do nothing
if (empty($details['created_on']) && empty($details['modified_on'])) {
@ -414,7 +414,6 @@ foreach ($categories as $item) {
$dsp_default_view = '';
$dsp_debug = '';
$dsp_order = '';
$progress = 0;
if (!$isInvitee) {
$progress = isset($progressList[$id]) && !empty($progressList[$id]) ? $progressList[$id] : 0;
@ -1028,6 +1027,8 @@ $template->assign('data', $data);
$template->assign('lp_is_shown', $lpIsShown);
$template->assign('filtered_category', $filteredCategoryId);
$template->assign('allow_min_time', $allowMinTime);
$template->assign('allow_dates_for_student', $allowDatesForStudent);
$templateName = $template->get_template('learnpath/list.tpl');
$content = $template->fetch($templateName);
$template->assign('content', $content);

@ -237,11 +237,10 @@ if ($debug) {
$get_toc_list = $lp->get_toc();
$get_teacher_buttons = $lp->get_teacher_toc_buttons();
$type_quiz = false;
$itemType = '';
foreach ($get_toc_list as $toc) {
if ($toc['id'] == $lp_item_id && $toc['type'] == 'quiz') {
$type_quiz = true;
if ($toc['id'] == $lp_item_id) {
$itemType = $toc['type'];
}
}
@ -260,6 +259,11 @@ if (!isset($src)) {
$get_toc_list
);
if (empty($src)) {
$src = 'blank.php?'.api_get_cidreq().'&error=document_protected';
break;
}
// Prevents FF 3.6 + Adobe Reader 9 bug see BT#794 when calling a pdf file in a LP.
$file_info = parse_url($src);
if (isset($file_info['path'])) {
@ -326,7 +330,6 @@ $autostart = 'true';
// Update status, total_time from lp_item_view table when you finish the exercises in learning path.
if ($debug) {
error_log('$type_quiz: '.$type_quiz);
error_log('$_REQUEST[exeId]: '.intval($_REQUEST['exeId']));
error_log('$lp_id: '.$lp_id);
error_log('$_REQUEST[lp_item_id]: '.intval($_REQUEST['lp_item_id']));
@ -349,14 +352,13 @@ if (!empty($_REQUEST['exeId']) &&
if (!empty($safe_id) && !empty($safe_item_id)) {
Exercise::saveExerciseInLp($safe_item_id, $safe_exe_id);
}
if (intval($_GET['fb_type']) != EXERCISE_FEEDBACK_TYPE_END) {
/*if (intval($_GET['fb_type']) != EXERCISE_FEEDBACK_TYPE_END) {
$src = 'blank.php?msg=exerciseFinished&'.api_get_cidreq(true, true, 'learnpath');
} else {
} else {*/
$src = api_get_path(WEB_CODE_PATH).'exercise/result.php?id='.$safe_exe_id.'&'.api_get_cidreq(true, true, 'learnpath');
if ($debug) {
error_log('Calling URL: '.$src);
}
}
$autostart = 'false';
}
@ -617,9 +619,9 @@ $template->assign(
)
);
$frameReady = Display::getFrameReadyBlock('#content_id, #content_id_blank');
$frameReady = Display::getFrameReadyBlock('#content_id, #content_id_blank', $itemType);
$template->assign('frame_ready', $frameReady);
$view = $template->get_template('learnpath/view.tpl');
$content = $template->fetch($view);

@ -573,10 +573,10 @@ if (user_is_online($student_id)) {
$avg_student_progress = $avg_student_score = 0;
if (empty($sessionId)) {
$isSubscribedToCourse = CourseManager::is_user_subscribed_in_course($user_info['user_id'], $courseCode);
$isSubscribedToCourse = CourseManager::is_user_subscribed_in_course($student_id, $courseCode);
} else {
$isSubscribedToCourse = CourseManager::is_user_subscribed_in_course(
$user_info['user_id'],
$student_id,
$courseCode,
true,
$sessionId
@ -585,7 +585,7 @@ if (empty($sessionId)) {
if ($isSubscribedToCourse) {
$avg_student_progress = Tracking::get_avg_student_progress(
$user_info['user_id'],
$student_id,
$courseCode,
[],
$sessionId
@ -593,7 +593,7 @@ if ($isSubscribedToCourse) {
// the score inside the Reporting table
$avg_student_score = Tracking::get_avg_student_score(
$user_info['user_id'],
$student_id,
$courseCode,
[],
$sessionId
@ -605,7 +605,7 @@ $time_spent_on_the_course = 0;
if (!empty($courseInfo)) {
$time_spent_on_the_course = api_time_to_hms(
Tracking::get_time_spent_on_the_course(
$user_info['user_id'],
$student_id,
$courseInfo['real_id'],
$sessionId
)
@ -613,12 +613,12 @@ if (!empty($courseInfo)) {
}
// get information about connections on the platform by student
$first_connection_date = Tracking::get_first_connection_date($user_info['user_id']);
$first_connection_date = Tracking::get_first_connection_date($student_id);
if ($first_connection_date == '') {
$first_connection_date = get_lang('NoConnexion');
}
$last_connection_date = Tracking::get_last_connection_date($user_info['user_id'], true);
$last_connection_date = Tracking::get_last_connection_date($student_id, true);
if ($last_connection_date == '') {
$last_connection_date = get_lang('NoConnexion');
}
@ -661,15 +661,15 @@ $csv_content[] = [
$coachs_name = '';
$session_name = '';
$userPicture = UserManager::getUserPicture($user_info['user_id'], USER_IMAGE_SIZE_BIG);
$userPicture = UserManager::getUserPicture($student_id, USER_IMAGE_SIZE_BIG);
$userGroupManager = new UserGroup();
$userGroups = $userGroupManager->getNameListByUser(
$user_info['user_id'],
$student_id,
UserGroup::NORMAL_CLASS
);
$userInfo = [
'id' => $user_info['user_id'],
'id' => $student_id,
'complete_name' => $user_info['complete_name'],
'complete_name_link' => $user_info['complete_name_with_message_link'],
'phone' => $user_info['phone'],
@ -704,10 +704,7 @@ if (!empty($courseCode)) {
// Display timezone if the user selected one and if the admin allows the use of user's timezone
$timezone = null;
$timezone_user = UserManager::get_extra_user_data_by_field(
$user_info['user_id'],
'timezone'
);
$timezone_user = UserManager::get_extra_user_data_by_field($student_id, 'timezone');
$use_users_timezone = api_get_setting('use_users_timezone', 'timezones');
if ($timezone_user['timezone'] != null && $use_users_timezone === 'true') {
$timezone = $timezone_user['timezone'];
@ -727,7 +724,7 @@ $userInfo['student_progress'] = (float) $avg_student_progress;
$userInfo['first_connection'] = $first_connection_date;
$userInfo['last_connection'] = $last_connection_date;
$userInfo['last_connection_in_course'] = api_format_date(
Tracking::getLastConnectionInAnyCourse($user_info['user_id']),
Tracking::getLastConnectionInAnyCourse($student_id),
DATE_FORMAT_SHORT
);
if ($details === 'true') {
@ -933,11 +930,32 @@ $content = $tpl->fetch($templateName);
echo $content;
// Careers.
if (api_get_configuration_value('allow_career_users')) {
$careers = UserManager::getUserCareers($student_id);
if (!empty($careers)) {
echo '<br /><br />';
echo Display::page_subheader(get_lang('Careers'), null, 'h3', ['class' => 'section-title']);
$table = new HTML_Table(['class' => 'table table-hover table-striped data_table']);
$table->setHeaderContents(0, 0, get_lang('Career'));
$table->setHeaderContents(0, 1, get_lang('Diagram'));
$row = 1;
foreach ($careers as $careerData) {
$table->setCellContents($row, 0, $careerData['name']);
$url = api_get_path(WEB_CODE_PATH).'user/career_diagram.php?career_id='.$careerData['id'];
$diagram = Display::url(get_lang('Diagram'), $url);
$table->setCellContents($row, 1, $diagram);
$row++;
}
echo $table->toHtml();
}
}
$allowAll = api_get_configuration_value('allow_teacher_access_student_skills');
if ($allowAll) {
// Show all skills
echo Tracking::displayUserSkills(
$user_info['user_id'],
$student_id,
0,
0,
true
@ -945,7 +963,7 @@ if ($allowAll) {
} else {
// Default behaviour - Show all skills depending the course and session id
echo Tracking::displayUserSkills(
$user_info['user_id'],
$student_id,
$courseInfo ? $courseInfo['real_id'] : 0,
$sessionId
);
@ -1071,7 +1089,7 @@ if (empty($details)) {
if ($isSubscribed) {
$timeInSeconds = Tracking::get_time_spent_on_the_course(
$user_info['user_id'],
$student_id,
$courseId,
$sId
);
@ -1136,7 +1154,7 @@ if (empty($details)) {
}
$progress = Tracking::get_avg_student_progress(
$user_info['user_id'],
$student_id,
$courseCodeItem,
[],
$sId
@ -1145,7 +1163,7 @@ if (empty($details)) {
$totalProgress += $progress;
$score = Tracking::get_avg_student_score(
$user_info['user_id'],
$student_id,
$courseCodeItem,
[],
$sId
@ -1183,12 +1201,12 @@ if (empty($details)) {
<td>'.$attendances_faults_avg.'</td>
<td>'.$scoretotal_display.'</td>';
if (!empty($coachId)) {
echo '<td width="10"><a href="'.api_get_self().'?student='.$user_info['user_id']
echo '<td width="10"><a href="'.api_get_self().'?student='.$student_id
.'&details=true&course='.$courseInfoItem['code'].'&id_coach='.$coachId.'&origin='.$origin
.'&id_session='.$sId.'#infosStudent">'
.Display::return_icon('2rightarrow.png', get_lang('Details')).'</a></td>';
} else {
echo '<td width="10"><a href="'.api_get_self().'?student='.$user_info['user_id']
echo '<td width="10"><a href="'.api_get_self().'?student='.$student_id
.'&details=true&course='.$courseInfoItem['code'].'&origin='.$origin.'&id_session='.$sId
.'#infosStudent">'
.Display::return_icon('2rightarrow.png', get_lang('Details')).'</a></td>';
@ -1569,7 +1587,7 @@ if (empty($details)) {
$link = Display::url(
Display::return_icon('2rightarrow.png', get_lang('Details')),
'lp_tracking.php?cidReq='.$courseCode.'&course='.$courseCode.$from.'&origin='.$origin
.'&lp_id='.$lp_id.'&student_id='.$user_info['user_id'].'&id_session='.$sessionId
.'&lp_id='.$lp_id.'&student_id='.$student_id.'&id_session='.$sessionId
);
echo Display::tag('td', $link);
}
@ -1579,7 +1597,7 @@ if (empty($details)) {
if ($any_result === true) {
$url = 'myStudents.php?action=reset_lp&sec_token='.$token.'&cidReq='.$courseCode.'&course='
.$courseCode.'&details='.$details.'&origin='.$origin.'&lp_id='.$lp_id.'&student='
.$user_info['user_id'].'&details=true&id_session='.$sessionId;
.$student_id.'&details=true&id_session='.$sessionId;
echo Display::url(
Display::return_icon('clean.png', get_lang('Clean')),
$url,

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save