Minor - merge with 1.11.x

pull/2487/head
jmontoyaa 8 years ago
parent 7bef5879bd
commit 78e1642cb9
  1. 10
      main/badge/issued.php
  2. 2
      main/forum/forumfunction.inc.php
  3. 2
      main/inc/introductionSection.inc.php
  4. 4
      main/inc/lib/AnnouncementEmail.php
  5. 43
      main/inc/lib/AnnouncementManager.php
  6. 51
      main/inc/lib/ScheduledAnnouncement.php
  7. 16
      main/inc/lib/TicketManager.php
  8. 1
      main/inc/lib/agenda.lib.php
  9. 120
      main/inc/lib/api.lib.php
  10. 2
      main/inc/lib/attendance.lib.php
  11. 8
      main/inc/lib/auth.lib.php
  12. 596
      main/inc/lib/career.lib.php
  13. 48
      main/inc/lib/certificate.lib.php
  14. 93
      main/inc/lib/course.lib.php
  15. 7
      main/inc/lib/course_category.lib.php
  16. 36
      main/inc/lib/course_home.lib.php
  17. 2
      main/inc/lib/database.lib.php
  18. 14
      main/inc/lib/display.lib.php
  19. 366
      main/inc/lib/document.lib.php
  20. 2
      main/inc/lib/events.lib.php
  21. 310
      main/inc/lib/exercise.lib.php
  22. 380
      main/inc/lib/exercise_show_functions.lib.php
  23. 8
      main/inc/lib/export.lib.inc.php
  24. 78
      main/inc/lib/extra_field.lib.php
  25. 42
      main/inc/lib/extra_field_value.lib.php
  26. 193
      main/inc/lib/fileUpload.lib.php
  27. 54
      main/inc/lib/glossary.lib.php
  28. 44
      main/inc/lib/image.lib.php
  29. 64
      main/inc/lib/import.lib.php
  30. 21
      main/inc/lib/internationalization.lib.php
  31. 7
      main/inc/lib/javascript/ckeditor/config_js.php
  32. 656
      main/inc/lib/javascript/ckeditor/plugins/video/dialogs/video.js
  33. 7
      main/inc/lib/javascript/hotspot/js/hotspot.js
  34. 4
      main/inc/lib/javascript/svgedit/extensions/imagelib/groups.php
  35. 4
      main/inc/lib/javascript/svgedit/extensions/imagelib/index.php
  36. 2
      main/inc/lib/login.lib.php
  37. 52
      main/inc/lib/message.lib.php
  38. 7
      main/inc/lib/model.lib.php
  39. 26
      main/inc/lib/notebook.lib.php
  40. 36
      main/inc/lib/pdf.lib.php
  41. 34
      main/inc/lib/pear/HTML/QuickForm/Rule/Compare.php
  42. 2
      main/inc/lib/pear/HTML/QuickForm/advmultiselect.php
  43. 2
      main/inc/lib/pear/HTML/QuickForm/file.php
  44. 2
      main/inc/lib/pear/HTML/QuickForm/select.php
  45. 69
      main/inc/lib/sessionmanager.lib.php
  46. 105
      main/inc/lib/skill.lib.php
  47. 34
      main/inc/lib/social.lib.php
  48. 8
      main/inc/lib/sortable_table.class.php
  49. 10
      main/inc/lib/svg-edit/extensions/imagelib/groups.php
  50. 10
      main/inc/lib/svg-edit/extensions/imagelib/index.php
  51. 112
      main/inc/lib/table_sort.class.php
  52. 170
      main/inc/lib/text.lib.php
  53. 18
      main/inc/lib/thematic.lib.php
  54. 25
      main/inc/lib/tracking.lib.php
  55. 30
      main/inc/lib/usergroup.lib.php
  56. 79
      main/inc/lib/usermanager.lib.php
  57. 138
      main/inc/lib/webservices/Rest.php
  58. 328
      main/lp/learnpath.class.php
  59. 23
      main/messages/outbox.php
  60. 24
      main/mySpace/myStudents.php
  61. 4
      main/mySpace/works_in_session_report.php
  62. 4
      main/upload/form.scorm.php

@ -13,8 +13,8 @@ use Chamilo\CoreBundle\Entity\SkillRelUserComment;
*/
require_once __DIR__.'/../inc/global.inc.php';
$issue = isset($_REQUEST['issue']) ? intval($_REQUEST['issue']) : 0;
$userId = isset($_REQUEST['user']) ? intval($_REQUEST['user']) : 0;
$issue = isset($_REQUEST['issue']) ? (int) $_REQUEST['issue'] : 0;
$userId = isset($_REQUEST['user']) ? (int) $_REQUEST['user'] : 0;
if (empty($issue)) {
api_not_allowed(true);
@ -22,8 +22,6 @@ if (empty($issue)) {
$entityManager = Database::getManager();
$skillIssue = $entityManager->find('ChamiloCoreBundle:SkillRelUser', $issue);
$skillRepo = $entityManager->getRepository('ChamiloCoreBundle:Skill');
$skillLevelRepo = $entityManager->getRepository('ChamiloSkillBundle:Level');
if (!$skillIssue) {
Display::addFlash(
@ -36,6 +34,9 @@ if (!$skillIssue) {
exit;
}
$skillRepo = $entityManager->getRepository('ChamiloCoreBundle:Skill');
$skillLevelRepo = $entityManager->getRepository('ChamiloSkillBundle:Level');
$user = $skillIssue->getUser();
$skill = $skillIssue->getSkill();
@ -160,6 +161,7 @@ if ($profile) {
'profile' => $profileId,
]);
$profileLevels = [];
foreach ($levels as $level) {
$profileLevels[$level->getPosition()][$level->getId()] = $level->getName();
}

@ -5496,7 +5496,7 @@ function get_notifications_of_user($user_id = 0, $force = false)
if (!isset($_SESSION['forum_notification']) ||
$_SESSION['forum_notification']['course'] != $course_id ||
$force = true
$force == true
) {
$_SESSION['forum_notification']['course'] = $course_id;

@ -54,7 +54,7 @@ if (!empty($courseId)) {
}
$config = [
'ToolbarSet' => 'IntroductionTool',
'ToolbarSet' => 'Basic',
'Width' => '100%',
'Height' => '300',
];

@ -355,12 +355,12 @@ class AnnouncementEmail
public function logMailSent()
{
$id = $this->announcement('id');
$course_id = $this->course('real_id');
$courseId = $this->course('real_id');
$table = Database::get_course_table(TABLE_ANNOUNCEMENT);
$sql = "UPDATE $table SET
email_sent = 1
WHERE
c_id = $course_id AND
c_id = $courseId AND
id = $id AND
session_id = {$this->session_id}
";

@ -410,9 +410,10 @@ class AnnouncementManager
$html .= "<tr><th style='text-align:right'>$modify_icons</th></tr>";
}
$toUser = $itemProperty->getToUser();
$toUserId = !empty($toUser) ? $toUser->getId() : 0;
//$toUser = $itemProperty->getToUser();
//$toUserId = !empty($toUser) ? $toUser->getId() : 0;
// The user id is always the current one.
$toUserId = api_get_user_id();
$content = self::parse_content(
$toUserId,
$content,
@ -768,7 +769,6 @@ class AnnouncementManager
$courseId = api_get_course_int_id();
$tbl_item_property = Database::get_course_table(TABLE_ITEM_PROPERTY);
$tbl_announcement = Database::get_course_table(TABLE_ANNOUNCEMENT);
$id = intval($id);
$params = [
@ -824,14 +824,20 @@ class AnnouncementManager
if (is_array($send_to['groups'])) {
foreach ($send_to['groups'] as $group) {
$groupInfo = GroupManager::get_group_properties($group);
api_item_property_update(
$courseInfo,
TOOL_ANNOUNCEMENT,
$id,
'AnnouncementUpdated',
api_get_user_id(),
$groupInfo
);
if (empty($groupInfo)) {
// Probably the group id and iid are different try checking the iid
$groupInfo = GroupManager::get_group_properties($group, true);
}
if ($groupInfo) {
api_item_property_update(
$courseInfo,
TOOL_ANNOUNCEMENT,
$id,
'AnnouncementUpdated',
api_get_user_id(),
$groupInfo
);
}
}
}
@ -1067,26 +1073,27 @@ class AnnouncementManager
{
$table = Database::get_course_table(TABLE_ITEM_PROPERTY);
$tool = Database::escape_string($tool);
$id = intval($id);
$id = (int) $id;
$courseId = api_get_course_int_id();
$sql = "SELECT * FROM $table
$sql = "SELECT to_user_id, to_group_id FROM $table
WHERE c_id = $courseId AND tool='$tool' AND ref = $id";
$result = Database::query($sql);
$to = [];
while ($row = Database::fetch_array($result)) {
$to_group = $row['to_group_id'];
switch ($to_group) {
// This is the iid of c_group_info
$toGroup = $row['to_group_id'];
switch ($toGroup) {
// it was send to one specific user
case null:
$to[] = "USER:".$row['to_user_id'];
break;
// it was sent to everyone
case 0:
return "everyone";
return 'everyone';
break;
default:
$to[] = "GROUP:".$row['to_group_id'];
$to[] = "GROUP:".$toGroup;
}
}

@ -96,13 +96,14 @@ class ScheduledAnnouncement extends Model
/**
* Returns a Form validator Obj.
*
* @param int $id
* @param string $url
* @param string $action add, edit
* @param array $sessionInfo
*
* @return FormValidator form validator obj
*/
public function returnSimpleForm($url, $action, $sessionInfo = [])
public function returnSimpleForm($id, $url, $action, $sessionInfo = [])
{
$form = new FormValidator(
'announcement',
@ -114,6 +115,12 @@ class ScheduledAnnouncement extends Model
$form->addDateTimePicker('date', get_lang('Date'));
$form->addText('subject', get_lang('Subject'));
$form->addHtmlEditor('message', get_lang('Message'));
$extraField = new ExtraField('scheduled_announcement');
$extra = $extraField->addElements($form, $id);
$js = $extra['jquery_ready_content'];
$form->addHtml("<script> $(function() { $js }); </script> ");
$this->setTagsInForm($form);
$form->addCheckBox('sent', null, get_lang('MessageSent'));
@ -232,6 +239,12 @@ class ScheduledAnnouncement extends Model
$form->addHtml('</div>');
$form->addText('subject', get_lang('Subject'));
$form->addHtmlEditor('message', get_lang('Message'));
$extraField = new ExtraField('scheduled_announcement');
$extra = $extraField->addElements($form);
$js = $extra['jquery_ready_content'];
$form->addHtml("<script> $(function() { $js }); </script> ");
$this->setTagsInForm($form);
if ($action == 'edit') {
@ -243,6 +256,37 @@ class ScheduledAnnouncement extends Model
return $form;
}
/**
* @param int $id
*
* @return string
*/
public function getAttachmentToString($id)
{
$file = $this->getAttachment($id);
if (!empty($file) && !empty($file['value'])) {
//$file = api_get_uploaded_web_url('schedule_announcement', $id, basename($file['value']));
$url = api_get_path(WEB_UPLOAD_PATH).$file['value'];
return get_lang('Attachment').': '.Display::url(basename($file['value']), $url, ['target' => '_blank']);
}
return '';
}
/**
* @param int $id
*
* @return array
*/
public function getAttachment($id)
{
$extraFieldValue = new ExtraFieldValue('scheduled_announcement');
$attachment = $extraFieldValue->get_values_by_handler_and_field_variable($id, 'attachment');
return $attachment;
}
/**
* @param int $urlId
*
@ -277,6 +321,8 @@ class ScheduledAnnouncement extends Model
continue;
}
$attachments = $this->getAttachmentToString($result['id']);
self::update(['id' => $result['id'], 'sent' => 1]);
$subject = $result['subject'];
@ -299,7 +345,7 @@ class ScheduledAnnouncement extends Model
$progress = Tracking::get_avg_student_progress(
$user['user_id'],
$courseInfo['code'],
null,
[],
$sessionId
);
}
@ -345,6 +391,7 @@ class ScheduledAnnouncement extends Model
];
$message = str_replace(array_keys($tags), $tags, $message);
$message .= $attachments;
MessageManager::send_message(
$userInfo['user_id'],

@ -190,11 +190,11 @@ class TicketManager
*/
public static function addUsersToCategory($categoryId, $users)
{
$table = Database::get_main_table(TABLE_TICKET_CATEGORY_REL_USER);
if (empty($users) || empty($categoryId)) {
return false;
}
$table = Database::get_main_table(TABLE_TICKET_CATEGORY_REL_USER);
foreach ($users as $userId) {
if (self::userIsAssignedToCategory($userId, $categoryId) == false) {
$params = [
@ -704,7 +704,7 @@ class TicketManager
* @param $ticketId
* @param $message_id
*
* @return array
* @return bool
*/
public static function saveMessageAttachmentFile(
$file_attach,
@ -718,7 +718,6 @@ class TicketManager
stripslashes($file_attach['name']),
$file_attach['type']
);
$file_name = $file_attach['name'];
$table_support_message_attachments = Database::get_main_table(TABLE_TICKET_MESSAGE_ATTACHMENTS);
if (!filter_extension($new_file_name)) {
echo Display::return_message(
@ -1113,7 +1112,6 @@ class TicketManager
{
$id = (int) $id;
$em = Database::getManager();
$item = $em->getRepository('ChamiloTicketBundle:MessageAttachment')->find($id);
if ($item) {
return $item;
@ -1353,7 +1351,7 @@ class TicketManager
$onlyToUserId,
$titleEmail,
$messageEmail,
0,
0,
false,
false,
[],
@ -1543,14 +1541,14 @@ class TicketManager
}
/**
* @throws \Doctrine\DBAL\DBALException
* Close old tickets.
*/
public static function close_old_tickets()
{
$table_support_tickets = Database::get_main_table(TABLE_TICKET_TICKET);
$table = Database::get_main_table(TABLE_TICKET_TICKET);
$now = api_get_utc_datetime();
$userId = api_get_user_id();
$sql = "UPDATE $table_support_tickets
$sql = "UPDATE $table
SET
status_id = '".self::STATUS_CLOSE."',
sys_lastedit_user_id ='$userId',
@ -1667,7 +1665,7 @@ class TicketManager
OR user.username LIKE '%$keyword%') ";
}
}
//Search advanced
// Search advanced
if (isset($_GET['submit_advanced'])) {
$keyword_category = Database::escape_string(
trim($_GET['keyword_category'])

@ -1300,7 +1300,6 @@ class Agenda
}
}
}
break;
}

@ -149,6 +149,7 @@ define('TOOL_GRADEBOOK', 'gradebook');
define('TOOL_NOTEBOOK', 'notebook');
define('TOOL_ATTENDANCE', 'attendance');
define('TOOL_COURSE_PROGRESS', 'course_progress');
define('TOOL_PORTFOLIO', 'portfolio');
// CONSTANTS defining Chamilo interface sections
define('SECTION_CAMPUS', 'mycampus');
@ -279,15 +280,17 @@ define('USERNAME_PURIFIER_SHALLOW', '/\s/');
define('IS_WINDOWS_OS', api_is_windows_os());
// Checks for installed optional php-extensions.
define('INTL_INSTALLED', function_exists('intl_get_error_code')); // intl extension (from PECL), it is installed by default as of PHP 5.3.0
define('ICONV_INSTALLED', function_exists('iconv')); // iconv extension, for PHP5 on Windows it is installed by default.
// intl extension (from PECL), it is installed by default as of PHP 5.3.0.
define('INTL_INSTALLED', function_exists('intl_get_error_code'));
// iconv extension, for PHP5 on Windows it is installed by default.
define('ICONV_INSTALLED', function_exists('iconv'));
define('MBSTRING_INSTALLED', function_exists('mb_strlen')); // mbstring extension.
// Patterns for processing paths. // Examples:
// Patterns for processing paths. Examples.
define('REPEATED_SLASHES_PURIFIER', '/\/{2,}/'); // $path = preg_replace(REPEATED_SLASHES_PURIFIER, '/', $path);
define('VALID_WEB_PATH', '/https?:\/\/[^\/]*(\/.*)?/i'); // $is_valid_path = preg_match(VALID_WEB_PATH, $path);
define('VALID_WEB_SERVER_BASE', '/https?:\/\/[^\/]*/i'); // $new_path = preg_replace(VALID_WEB_SERVER_BASE, $new_base, $path);
// $new_path = preg_replace(VALID_WEB_SERVER_BASE, $new_base, $path);
define('VALID_WEB_SERVER_BASE', '/https?:\/\/[^\/]*/i');
// Constants for api_get_path() and api_get_path_type(), etc. - registered path types.
// basic (leaf elements)
define('REL_CODE_PATH', 'REL_CODE_PATH');
@ -2038,7 +2041,7 @@ function api_get_course_info($course_code = null, $strict = false)
*
* @return \Chamilo\CoreBundle\Entity\Course
*/
function api_get_course_entity($courseId)
function api_get_course_entity($courseId = 0)
{
if (empty($courseId)) {
$courseId = api_get_course_int_id();
@ -2047,6 +2050,20 @@ function api_get_course_entity($courseId)
return CourseManager::getManager()->find($courseId);
}
/**
* @param int $id
*
* @return \Chamilo\CoreBundle\Entity\Session
*/
function api_get_session_entity($id = 0)
{
if (empty($id)) {
$id = api_get_session_id();
}
return Database::getManager()->getRepository('ChamiloCoreBundle:Session')->find($id);
}
/**
* Returns the current course info array.
@ -2180,7 +2197,8 @@ function api_format_course_array($course_data)
null,
null,
null,
true
true,
false
);
}
$_course['course_image_large'] = $url_image;
@ -2597,11 +2615,11 @@ function api_get_session_image($session_id, $status_id)
if ((int) $status_id != 5) { //check whether is not a student
if ($session_id > 0) {
$session_img = "&nbsp;&nbsp;".Display::return_icon(
'star.png',
get_lang('SessionSpecificResource'),
['align' => 'absmiddle'],
ICON_SIZE_SMALL
);
'star.png',
get_lang('SessionSpecificResource'),
['align' => 'absmiddle'],
ICON_SIZE_SMALL
);
}
}
@ -2835,6 +2853,11 @@ function api_get_settings_params($params)
return $result;
}
/**
* @param array $params example: [id = ? => '1']
*
* @return array
*/
function api_get_settings_params_simple($params)
{
$table = Database::get_main_table(TABLE_MAIN_SETTINGS_CURRENT);
@ -3072,7 +3095,7 @@ function api_is_coach($session_id = 0, $courseId = null, $check_student_view = t
$session_table = Database::get_main_table(TABLE_MAIN_SESSION);
$session_rel_course_rel_user_table = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
$sessionIsCoach = null;
$sessionIsCoach = [];
if (!empty($courseId)) {
$sql = "SELECT DISTINCT s.id, name, access_start_date, access_end_date
@ -4088,17 +4111,17 @@ function api_item_property_update(
$to_group_id = 0;
if (!empty($groupInfo) && isset($groupInfo['iid'])) {
$to_group_id = $groupInfo['iid'];
$to_group_id = (int) $groupInfo['iid'];
}
$em = Database::getManager();
// Definition of variables.
$tool = Database::escape_string($tool);
$item_id = intval($item_id);
$item_id = (int) $item_id;
$lastEditTypeNoFilter = $last_edit_type;
$last_edit_type = Database::escape_string($last_edit_type);
$user_id = intval($user_id);
$user_id = (int) $user_id;
$startVisible = "NULL";
if (!empty($start_visible)) {
@ -4319,7 +4342,12 @@ function api_item_property_update(
$user_id = api_get_anonymous_id();
$objUser = api_get_user_entity($user_id);
}
$objGroup = $em->find('ChamiloCourseBundle:CGroupInfo', intval($to_group_id));
$objGroup = null;
if (!empty($to_group_id)) {
$objGroup = $em->find('ChamiloCourseBundle:CGroupInfo', $to_group_id);
}
$objToUser = api_get_user_entity($to_user_id);
$objSession = $em->find('ChamiloCoreBundle:Session', intval($session_id));
@ -4648,7 +4676,7 @@ function api_get_languages_combo($name = 'language')
* @param bool Hide form if only one language available (defaults to false = show the box anyway)
* @param bool $showAsButton
*
* @return string Display the box directly
* @return null|string Display the box directly
*/
function api_display_language_form($hide_if_no_choice = false, $showAsButton = false)
{
@ -6867,7 +6895,7 @@ function api_browser_support($format = '')
return false;
}
} elseif ($format == 'pdf') {
//native pdf support
// native pdf support
if ($current_browser == 'Chrome' && $current_majorver >= 6) {
$result[$format] = true;
@ -7103,6 +7131,9 @@ function api_get_asset($file)
/**
* Returns the <script> HTML tag.
*
* @param string $file
* @param string $media
*
* @return string
*/
function api_get_css_asset($file, $media = 'screen')
@ -7114,6 +7145,7 @@ function api_get_css_asset($file, $media = 'screen')
* Returns the <link> HTML tag.
*
* @param string $file
* @param string $media
*/
function api_get_css($file, $media = 'screen')
{
@ -7617,11 +7649,17 @@ function api_set_default_visibility(
$sessionId = empty($sessionId) ? api_get_session_id() : $sessionId;
$userId = empty($userId) ? api_get_user_id() : $userId;
if (empty($group_id)) {
$group_id = api_get_group_id();
// if group is null force group_id = 0, this force is needed to create a LP folder with group = 0
if (is_null($group_id)) {
$group_id = 0;
} else {
$group_id = empty($group_id) ? api_get_group_id() : $group_id;
}
$groupInfo = GroupManager::get_group_properties($group_id);
$groupInfo = [];
if (!empty($group_id)) {
$groupInfo = GroupManager::get_group_properties($group_id);
}
$original_tool_id = $tool_id;
switch ($tool_id) {
@ -9034,16 +9072,31 @@ function api_upload_file($type, $file, $itemId, $cropParameters = '')
*
* @return bool
*/
function api_get_uploaded_file($type, $itemId, $file)
function api_get_uploaded_web_url($type, $itemId, $file)
{
return api_get_uploaded_file($type, $itemId, $file, true);
}
/**
* @param string $type
* @param int $itemId
* @param string $file
* @param bool $getUrl
*
* @return bool
*/
function api_get_uploaded_file($type, $itemId, $file, $getUrl = false)
{
$itemId = (int) $itemId;
$pathId = '/'.substr((string) $itemId, 0, 1).'/'.$itemId.'/';
$path = api_get_path(SYS_UPLOAD_PATH).$type.$pathId;
$file = basename($file);
$file = $path.'/'.$file;
if (file_exists($file)) {
if (Security::check_abs_path($file, $path) && is_file($file) && file_exists($file)) {
if ($getUrl) {
return str_replace(api_get_path(SYS_UPLOAD_PATH), api_get_path(WEB_UPLOAD_PATH), $file);
}
return $file;
}
@ -9074,8 +9127,9 @@ function api_download_uploaded_file($type, $itemId, $file, $title = '')
*/
function api_remove_uploaded_file($type, $file)
{
$path = api_get_path(SYS_UPLOAD_PATH).$type.'/'.$file;
if (file_exists($path)) {
$typePath = api_get_path(SYS_UPLOAD_PATH).$type;
$path = $typePath.'/'.$file;
if (Security::check_abs_path($path, $typePath) && file_exists($path) && is_file($path)) {
unlink($path);
}
}
@ -9106,18 +9160,18 @@ function api_float_val($number)
* 3.141516 => 3.14
* 3,141516 => 3,14
*
* @todo WIP
*
* @param string $number number in iso code
* @param string $number number in iso code
* @param int $decimals
* @param string $decimalSeparator
* @param string $thousandSeparator
*
* @return bool|string
*/
function api_number_format($number, $decimals = 0)
function api_number_format($number, $decimals = 0, $decimalSeparator = '.', $thousandSeparator = ',')
{
$number = api_float_val($number);
return number_format($number, $decimals);
return number_format($number, $decimals, $decimalSeparator, $thousandSeparator);
}
/**

@ -43,7 +43,7 @@ class Attendance
*
* @see SortableTable#get_total_number_of_items()
*/
public static function get_number_of_attendances($active = -1)
public static function getNumberOfAttendances($active = -1)
{
$tbl_attendance = Database::get_course_table(TABLE_ATTENDANCE);
$session_id = api_get_session_id();

@ -686,7 +686,7 @@ class Auth
$form->addElement('hidden', 'sec_token', Security::getTokenFromSession());
$form->addElement('hidden', 'subscribe_user_with_password', $all_course_information['code']);
$form->addElement('text', 'course_registration_code');
$form->addButton('submit', get_lang('SubmitRegistrationCode'));
$form->addButtonSave(get_lang('SubmitRegistrationCode'));
$content = $form->returnForm();
return ['message' => $message, 'content' => $content];
@ -736,12 +736,8 @@ class Auth
$sql .= "LIMIT {$limit['start']}, {$limit['length']} ";
}
$ids = Database::store_result(
Database::query($sql)
);
$ids = Database::store_result(Database::query($sql));
$sessions = [];
foreach ($ids as $id) {
$sessions[] = $em->find('ChamiloCoreBundle:Session', $id);
}

@ -312,6 +312,7 @@ class Career extends Model
if (!($graph instanceof Graph)) {
return '';
}
// Getting max column
$maxColumn = 0;
foreach ($graph->getVertices() as $vertex) {
@ -328,7 +329,6 @@ class Career extends Model
$groupData = explode(':', $group);
$group = $groupData[0];
$groupLabel = isset($groupData[1]) ? $groupData[1] : '';
$subGroup = $vertex->getAttribute('SubGroup');
$subGroupData = explode(':', $subGroup);
$column = $vertex->getGroup();
@ -349,7 +349,6 @@ class Career extends Model
$connections = '';
$groupDrawLine = [];
$groupCourseList = [];
// Read Connections column
foreach ($list as $group => $subGroupList) {
foreach ($subGroupList as $subGroupData) {
@ -453,6 +452,583 @@ class Career extends Model
return $graphHtml;
}
/**
* @param Graph $graph
* @param Template $tpl
*
* @return string
*/
public static function renderDiagramByColumn($graph, $tpl)
{
if (!($graph instanceof Graph)) {
return '';
}
// Getting max column
$maxColumn = 0;
foreach ($graph->getVertices() as $vertex) {
$groupId = (int) $vertex->getGroup();
if ($groupId > $maxColumn) {
$maxColumn = $groupId;
}
}
$list = [];
$subGroups = [];
/** @var Vertex $vertex */
foreach ($graph->getVertices() as $vertex) {
$column = $vertex->getGroup();
$group = $vertex->getAttribute('Group');
$groupData = explode(':', $group);
$group = $groupData[0];
$groupLabel = isset($groupData[1]) ? $groupData[1] : '';
$subGroup = $vertex->getAttribute('SubGroup');
$subGroupData = explode(':', $subGroup);
$row = $vertex->getAttribute('Row');
$subGroupId = $subGroupData[0];
$subGroupLabel = isset($subGroupData[1]) ? $subGroupData[1] : '';
if (!empty($subGroupId) && !in_array($subGroupId, $subGroups)) {
//$subGroups[$subGroupId][] = $vertex->getId();
$subGroups[$subGroupId]['items'][] = $vertex->getId();
$subGroups[$subGroupId]['label'] = $subGroupLabel;
}
$list[$column]['rows'][$row]['items'][] = $vertex;
$list[$column]['rows'][$row]['label'] = $subGroupId;
$list[$column]['rows'][$row]['group'] = $group;
$list[$column]['rows'][$row]['group_label'] = $groupLabel;
$list[$column]['rows'][$row]['subgroup'] = $subGroup;
$list[$column]['rows'][$row]['subgroup_label'] = $subGroupLabel;
//$list[$column]['label'] = $subGroupLabel;
$list[$column]['label'] = $groupLabel;
$list[$column]['column'] = $column;
}
$connections = '';
$groupDrawLine = [];
$groupCourseList = [];
$simpleConnectionList = [];
// Read Connections column
foreach ($list as $column => $groupList) {
foreach ($groupList['rows'] as $subGroupList) {
/** @var Vertex $vertex */
foreach ($subGroupList['items'] as $vertex) {
if ($vertex instanceof Vertex) {
$rowId = $vertex->getId();
$groupCourseList[$vertex->getAttribute('Column')][] = $vertex->getId();
$connectionList = $vertex->getAttribute('Connections');
if (empty($connectionList)) {
continue;
}
$firstConnection = '';
$secondConnection = '';
$simpleFirstConnection = '';
$simpleSecondConnection = '';
$explode = explode('-', $connectionList);
$pos = strpos($explode[0], 'SG');
if ($pos === false) {
$pos = strpos($explode[0], 'G');
if (is_numeric($pos)) {
// group_123 id
$groupValueId = (int) str_replace(
'G',
'',
$explode[0]
);
$secondConnection = 'group_'.$groupValueId;
$firstConnection = 'row_'.(int) $rowId;
$groupDrawLine[$groupValueId] = true;
$simpleSecondConnection = 'g'.$groupValueId;
$simpleFirstConnection = 'v'.(int) $rowId;
} else {
// Course block (row_123 id)
if (!empty($explode[0])) {
$firstConnection = 'row_'.(int) $explode[0];
$simpleFirstConnection = 'v'.(int) $explode[0];
}
}
} else {
// subgroup__123 id
$firstConnection = 'subgroup_'.(int) str_replace('SG', '', $explode[0]);
$simpleFirstConnection = 'sg'.(int) str_replace('SG', '', $explode[0]);
}
$pos = false;
if (isset($explode[1])) {
$pos = strpos($explode[1], 'SG');
}
if ($pos === false) {
if (isset($explode[1])) {
$pos = strpos($explode[1], 'G');
$value = $explode[1];
}
if (is_numeric($pos)) {
$groupValueId = (int) str_replace(
'G',
'',
$value
);
$secondConnection = 'group_'.$groupValueId;
$simpleSecondConnection = 'g'.(int) $groupValueId;
$groupDrawLine[$groupValueId] = true;
} else {
// Course block (row_123 id)
if (!empty($explode[0]) && isset($explode[1])) {
$secondConnection = 'row_'.(int) $explode[1];
$simpleSecondConnection = 'v'.(int) $explode[1];
}
}
} else {
$secondConnection = 'subgroup_'.(int) str_replace('SG', '', $explode[1]);
$simpleSecondConnection = 'sg'.(int) str_replace('SG', '', $explode[1]);
}
if (!empty($firstConnection) && !empty($firstConnection)) {
$simpleConnectionList[] = [
'from' => $simpleFirstConnection,
'to' => $simpleSecondConnection,
];
$connections .= self::createConnection(
$firstConnection,
$secondConnection,
['Left', 'Right']
);
}
}
}
}
}
$graphHtml = '<div id ="career_grid">';
$groupsBetweenColumns = [];
foreach ($list as $column => $columnList) {
foreach ($columnList['rows'] as $subGroupList) {
$newGroup = $subGroupList['group'];
$label = $subGroupList['group_label'];
$newOrder[$newGroup]['items'][] = $subGroupList;
$newOrder[$newGroup]['label'] = $label;
$groupsBetweenColumns[$newGroup][] = $subGroupList;
}
}
// Creates graph
$graph = new stdClass();
$graph->blockWidth = 240;
$graph->blockHeight = 120;
$graph->xGap = 70;
$graph->yGap = 40;
$graph->xDiff = 70;
$graph->yDiff = 40;
$graph->groupXGap = 50;
foreach ($groupsBetweenColumns as $group => $items) {
self::parseColumnList($groupCourseList, $items, '', $graph, $simpleConnectionList);
}
$graphHtml .= '</div>';
// $graphHtml .= $connections;
$graphHtml .= '<style>
panel-title
#career_grid {
display: grid;
grid-gap: 40px;
grid-template-columns: repeat(6, [col] auto ) ;
grid-template-rows: repeat(2, [row] auto);
background-color: #fff;
color: #444;
justify-items: stretch;
align-items: start;
align-content: start;
justify-content: stretch;
}
.group_class {
border: solid 2px;
padding: 8px;
}
.panel-title {
font-size: 11px;
}
</style>
';
// Create groups
if (!empty($graph->groupList)) {
$groupList = [];
$groupDiffX = 20;
$groupDiffY = 10;
$style = 'whiteSpace=wrap;rounded;strokeColor=red;fillColor=none;strokeWidth=2;align=left;verticalAlign=top;';
foreach ($graph->groupList as $id => $data) {
if (empty($id)) {
continue;
}
$x = $data['min_x'] - $groupDiffX;
$y = $data['min_y'] - $groupDiffY;
$width = $data['max_width'] + ($groupDiffX * 2);
$height = $data['max_height'] + $groupDiffY * 2;
$label = '<h4>'.$data['label'].'</h4>';
$vertexData = "var g$id = graph.insertVertex(parent, null, '$label', $x, $y, $width, $height, '$style');";
$groupList[] = $vertexData;
}
$tpl->assign('group_list', $groupList);
}
// Create subgroups
$subGroupList = [];
$subGroupListData = [];
foreach ($subGroups as $subGroupId => $vertexData) {
$label = $vertexData['label'];
$vertexIdList = $vertexData['items'];
foreach ($vertexIdList as $rowId) {
$data = $graph->allData[$rowId];
$originalRow = $data['row'];
$column = $data['column'];
$x = $data['x'];
$y = $data['y'];
$width = $data['width'];
$height = $data['height'];
if (!isset($subGroupListData[$subGroupId])) {
$subGroupListData[$subGroupId]['min_x'] = 1000;
$subGroupListData[$subGroupId]['min_y'] = 1000;
$subGroupListData[$subGroupId]['max_width'] = 0;
$subGroupListData[$subGroupId]['max_height'] = 0;
$subGroupListData[$subGroupId]['label'] = $label;
}
if ($x < $subGroupListData[$subGroupId]['min_x']) {
$subGroupListData[$subGroupId]['min_x'] = $x;
}
if ($y < $subGroupListData[$subGroupId]['min_y']) {
$subGroupListData[$subGroupId]['min_y'] = $y;
}
/*$originalRow--;
$column--;*/
$subGroupListData[$subGroupId]['max_width'] = ($column + 1) * ($width + $graph->xGap) - $subGroupListData[$subGroupId]['min_x'];
$subGroupListData[$subGroupId]['max_height'] = ($originalRow + 1) * ($height + $graph->yGap) - $subGroupListData[$subGroupId]['min_y'];
}
$style = 'whiteSpace=wrap;rounded;dashed=1;strokeColor=blue;fillColor=none;strokeWidth=2;align=left;verticalAlign=bottom;';
$subGroupDiffX = 5;
foreach ($subGroupListData as $subGroupId => $data) {
$x = $data['min_x'] - $subGroupDiffX;
$y = $data['min_y'] - $subGroupDiffX;
$width = $data['max_width'] + $subGroupDiffX * 2;
$height = $data['max_height'] + $subGroupDiffX * 2;
$label = '<h4>'.$data['label'].'</h4>';
$vertexData = "var sg$subGroupId = graph.insertVertex(parent, null, '$label', $x, $y, $width, $height, '$style');";
$subGroupList[] = $vertexData;
}
}
// Create connections (arrows)
if (!empty($simpleConnectionList)) {
$connectionList = [];
//$style = 'endArrow=classic;html=1;strokeWidth=4;exitX=1;exitY=0.5;entryX=0;entryY=0.5;';
$style = '';
foreach ($simpleConnectionList as $connection) {
$from = $connection['from'];
$to = $connection['to'];
$vertexData = "var e1 = graph.insertEdge(parent, null, '', $from, $to, '$style')";
$connectionList[] = $vertexData;
}
$tpl->assign('connections', $connectionList);
}
$tpl->assign('subgroup_list', $subGroupList);
$tpl->assign('vertex_list', $graph->elementList);
$graphHtml .= '<div id="graphContainer"></div>';
return $graphHtml;
}
public static function parseColumnList($groupCourseList, $columnList, $width, &$graph, &$connections)
{
$graphHtml = '';
$oldGroup = null;
$newOrder = [];
foreach ($columnList as $key => $subGroupList) {
$newGroup = $subGroupList['group'];
$label = $subGroupList['group_label'];
$newOrder[$newGroup]['items'][] = $subGroupList;
$newOrder[$newGroup]['label'] = $label;
}
foreach ($newOrder as $newGroup => $data) {
$label = $data['label'];
$subGroupList = $data['items'];
if (!isset($graph->groupList[$newGroup])) {
$graph->groupList[$newGroup]['min_x'] = 1000;
$graph->groupList[$newGroup]['min_y'] = 1000;
$graph->groupList[$newGroup]['max_width'] = 0;
$graph->groupList[$newGroup]['max_height'] = 0;
$graph->groupList[$newGroup]['label'] = $label;
}
$maxColumn = 0;
$maxRow = 0;
$minColumn = 100;
$minRow = 100;
foreach ($subGroupList as $item) {
/** @var Vertex $vertex */
foreach ($item['items'] as $vertex) {
$column = $vertex->getAttribute('Column');
$realRow = $vertex->getAttribute('Row');
if ($column > $maxColumn) {
$maxColumn = $column;
}
if ($realRow > $maxRow) {
$maxRow = $realRow;
}
if ($column < $minColumn) {
$minColumn = $column;
}
if ($realRow < $minRow) {
$minRow = $realRow;
}
}
}
if (!empty($newGroup)) {
$graphHtml .= '<div
id ="group_'.$newGroup.'"
class="group'.$newGroup.' group_class"
style="display:grid;
align-self: start;
grid-gap: 10px;
justify-items: stretch;
align-items: start;
align-content: start;
justify-content: stretch;
grid-area:'.$minRow.'/'.$minColumn.'/'.$maxRow.'/'.$maxColumn.'">'; //style="display:grid"
}
$addRow = 0;
if (!empty($label)) {
$graphHtml .= "<div class='my_label' style='grid-area:$minRow/$minColumn/$maxRow/$maxColumn'>$label</div>";
$addRow = 1;
}
foreach ($subGroupList as $item) {
$graphHtml .= self::parseVertexList(
$groupCourseList,
$item['items'],
$addRow,
$graph,
$newGroup,
$connections
);
}
if (!empty($newGroup)) {
$graphHtml .= '</div >';
}
}
return $graphHtml;
}
public static function parseVertexList($groupCourseList, $vertexList, $addRow = 0, &$graph, $group, &$connections)
{
if (empty($vertexList)) {
return '';
}
$graphHtml = '';
/** @var Vertex $vertex */
foreach ($vertexList as $vertex) {
$column = $vertex->getAttribute('Column');
$realRow = $originalRow = $vertex->getAttribute('Row');
if ($addRow) {
$realRow = $realRow + $addRow;
}
$id = $vertex->getId();
$area = "$realRow/$column";
$graphHtml .= '<div
id = "row_wrapper_'.$id.'"
data= "'.$originalRow.'-'.$column.'"
style="
align-self: start;
justify-content: stretch;
grid-area:'.$area.'"
>';
$color = '';
if (!empty($vertex->getAttribute('DefinedColor'))) {
$color = $vertex->getAttribute('DefinedColor');
}
$content = '<div class="pull-left">'.$vertex->getAttribute('Notes').'</div>';
$content .= '<div class="pull-right">['.$id.']</div>';
$title = $vertex->getAttribute('graphviz.label');
if (!empty($vertex->getAttribute('LinkedElement'))) {
$title = Display::url($title, $vertex->getAttribute('LinkedElement'));
}
$originalRow--;
$column--;
//$title = "$originalRow / $column";
$graphHtml .= Display::panel(
$content,
$title,
null,
null,
null,
"row_$id",
$color
);
$panel = Display::panel(
$content,
$title,
null,
null,
null,
"row_$id",
$color
);
$x = $column * $graph->blockWidth + $graph->xDiff;
$y = $originalRow * $graph->blockHeight + $graph->yDiff;
$width = $graph->blockWidth - $graph->xGap;
$height = $graph->blockHeight - $graph->yGap;
$style = 'text;html=1;strokeColor=green;fillColor=#ffffff;overflow=fill;rounded=0;align=left;';
$panel = str_replace(["\n", "\r"], '', $panel);
//$panel = $title.' - '.$group.' row: '.$originalRow.' heigh: '.$height.' id: '.$id;
$vertexData = "var v$id = graph.insertVertex(parent, null, '".addslashes($panel)."', $x, $y, $width, $height, '$style');";
$graph->elementList[$id] = $vertexData;
$graph->allData[$id] = [
'x' => $x,
'y' => $y,
'width' => $width,
'height' => $height,
'row' => $originalRow,
'column' => $column,
'label' => $title,
];
if ($x < $graph->groupList[$group]['min_x']) {
$graph->groupList[$group]['min_x'] = $x;
}
if ($y < $graph->groupList[$group]['min_y']) {
$graph->groupList[$group]['min_y'] = $y;
}
$graph->groupList[$group]['max_width'] = ($column + 1) * ($width + $graph->xGap) - $graph->groupList[$group]['min_x'];
$graph->groupList[$group]['max_height'] = ($originalRow + 1) * ($height + ($graph->yGap)) - $graph->groupList[$group]['min_y'];
$graphHtml .= '</div>';
$arrow = $vertex->getAttribute('DrawArrowFrom');
$found = false;
if (!empty($arrow)) {
$pos = strpos($arrow, 'SG');
if ($pos === false) {
$pos = strpos($arrow, 'G');
if (is_numeric($pos)) {
$parts = explode('G', $arrow);
if (empty($parts[0]) && count($parts) == 2) {
$groupArrow = $parts[1];
$graphHtml .= self::createConnection(
"group_$groupArrow",
"row_$id",
['Left', 'Right']
);
$found = true;
$connections[] = [
'from' => "g$groupArrow",
'to' => "v$id",
];
}
}
} else {
// Case is only one subgroup value example: SG1
$parts = explode('SG', $arrow);
if (empty($parts[0]) && count($parts) == 2) {
$subGroupArrow = $parts[1];
$graphHtml .= self::createConnection(
"subgroup_$subGroupArrow",
"row_$id",
['Left', 'Right']
);
$found = true;
$connections[] = [
'from' => "sg$subGroupArrow",
'to' => "v$id",
];
}
}
if ($found == false) {
// case is connected to 2 subgroups: Example SG1-SG2
$parts = explode('-', $arrow);
if (count($parts) == 2 && !empty($parts[0]) && !empty($parts[1])) {
$defaultArrow = ['Top', 'Bottom'];
$firstPrefix = '';
$firstId = '';
$secondId = '';
$secondPrefix = '';
if (is_numeric($pos = strpos($parts[0], 'SG'))) {
$firstPrefix = 'sg';
$firstId = str_replace('SG', '', $parts[0]);
}
if (is_numeric($pos = strpos($parts[1], 'SG'))) {
$secondPrefix = 'sg';
$secondId = str_replace('SG', '', $parts[1]);
}
if (!empty($secondId) && !empty($firstId)) {
$connections[] = [
'from' => $firstPrefix.$firstId,
'to' => $secondPrefix.$secondId,
$defaultArrow,
];
$found = true;
}
}
}
if ($found == false) {
// case DrawArrowFrom is an integer
$defaultArrow = ['Left', 'Right'];
if (isset($groupCourseList[$column]) &&
in_array($arrow, $groupCourseList[$column])
) {
$defaultArrow = ['Top', 'Bottom'];
}
$graphHtml .= self::createConnection(
"row_$arrow",
"row_$id",
$defaultArrow
);
$connections[] = [
'from' => "v$arrow",
'to' => "v$id",
];
}
}
}
return $graphHtml;
}
/**
* @param array $groupCourseList list of groups and their courses
* @param int $group
@ -480,8 +1056,10 @@ class Career extends Model
$groupIdTag = "group_$group";
$borderLine = $showGroupLine === true ? 'border-style:solid;' : '';
// padding:15px;
$graphHtml = '<div id="'.$groupIdTag.'" class="career_group" style=" '.$borderLine.' padding:15px; float:left; margin-left:'.$leftGroup.'; width:'.$widthGroup.'%">';
$graphHtml = '<div
id="'.$groupIdTag.'" class="career_group"
style=" '.$borderLine.' padding:15px; float:left; margin-left:'.$leftGroup.'; width:'.$widthGroup.'%">';
if (!empty($groupLabel)) {
$graphHtml .= '<h3>'.$groupLabel.'</h3>';
@ -501,7 +1079,9 @@ class Career extends Model
}
// padding:15px;
$graphHtml .= '<div id="subgroup_'.$subGroup.'" class="career_subgroup" style="'.$line.' margin-bottom:20px; padding:15px; float:left; margin-left:0px; width:100%">';
$graphHtml .= '<div
id="subgroup_'.$subGroup.'" class="career_subgroup"
style="'.$line.' margin-bottom:20px; padding:15px; float:left; margin-left:0px; width:100%">';
if (!empty($subGroupLabel)) {
$graphHtml .= '<h3>'.$subGroupLabel.'</h3>';
}
@ -515,7 +1095,9 @@ class Career extends Model
}
$widthColumn = 85 / count($columnList);
$graphHtml .= '<div id="col_'.$column.'" class="career_column" style="padding:15px;float:left; margin-left:'.$leftColumn.'; width:'.$widthColumn.'%">';
$graphHtml .= '<div
id="col_'.$column.'" class="career_column"
style="padding:15px;float:left; margin-left:'.$leftColumn.'; width:'.$widthColumn.'%">';
$maxRow = 0;
foreach ($rows as $row => $vertex) {
if ($row > $maxRow) {
@ -613,8 +1195,6 @@ class Career extends Model
$parts = explode('SG', $arrow);
if (empty($parts[0]) && count($parts) == 2) {
$subGroupArrow = $parts[1];
/*var_dump($subGroupArrow);
var_dump(array_keys($subGroupList));*/
$graphHtml .= self::createConnection(
"subgroup_$subGroupArrow",
"row_$id",

@ -40,16 +40,18 @@ class Certificate extends Model
/**
* Constructor.
*
* @param int $certificate_id ID of the certificate
* @param int $certificate_id ID of the certificate
* @param int $userId
* @param bool $sendNotification send message to student
* @param bool $sendNotification send message to student
* @param bool $updateCertificateData
*
* If no ID given, take user_id and try to generate one
*/
public function __construct(
$certificate_id = 0,
$userId = 0,
$sendNotification = false
$sendNotification = false,
$updateCertificateData = true
) {
$this->table = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CERTIFICATE);
$this->user_id = !empty($userId) ? $userId : api_get_user_id();
@ -119,7 +121,8 @@ class Certificate extends Model
self::updateUserCertificateInfo(
0,
$this->user_id,
$path_certificate
$path_certificate,
$updateCertificateData
);
$this->certificate_data['path_certificate'] = $path_certificate;
@ -453,22 +456,24 @@ class Certificate extends Model
/**
* Update user info about certificate.
*
* @param int $cat_id category id
* @param int $user_id user id
* @param string $path_certificate the path name of the certificate
* @param int $categoryId category id
* @param int $user_id user id
* @param string $path_certificate the path name of the certificate
* @param bool $updateCertificateData
*/
public function updateUserCertificateInfo(
$cat_id,
$categoryId,
$user_id,
$path_certificate
$path_certificate,
$updateCertificateData = true
) {
$table = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CERTIFICATE);
$now = api_get_utc_datetime();
if (!UserManager::is_user_certified($cat_id, $user_id)) {
if (!UserManager::is_user_certified($categoryId, $user_id) && $updateCertificateData) {
$table = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CERTIFICATE);
$now = api_get_utc_datetime();
$sql = 'UPDATE '.$table.' SET
path_certificate="'.Database::escape_string($path_certificate).'",
created_at = "'.$now.'"
WHERE cat_id="'.intval($cat_id).'" AND user_id="'.intval($user_id).'" ';
WHERE cat_id="'.intval($categoryId).'" AND user_id="'.intval($user_id).'" ';
Database::query($sql);
}
}
@ -677,7 +682,6 @@ class Certificate extends Model
0,
$this->user_id
);
if (empty($myCertificate)) {
GradebookUtils::registerUserInfoAboutCertificate(
0,
@ -691,7 +695,6 @@ class Certificate extends Model
$extraFieldValue = new ExtraFieldValue('user');
$value = $extraFieldValue->get_values_by_handler_and_field_variable($this->user_id, 'legal_accept');
$termsValidationDate = '';
if (isset($value) && !empty($value['value'])) {
list($id, $id2, $termsValidationDate) = explode(':', $value['value']);
@ -716,16 +719,9 @@ class Certificate extends Model
if (isset($gradebookCategories[0])) {
/** @var Category $category */
$category = $gradebookCategories[0];
// $categoryId = $category->get_id();
// @todo how we check if user pass a gradebook?
//$certificateInfo = GradebookUtils::get_certificate_by_user_id($categoryId, $this->user_id);
$result = Category::userFinishedCourse(
$this->user_id,
$category,
null,
$courseInfo['code'],
$session['session_id'],
true
);
@ -742,8 +738,12 @@ class Certificate extends Model
}
$skill = new Skill();
$skills = $skill->getStudentSkills($this->user_id);
$timeInSeconds = Tracking::get_time_spent_on_the_platform($this->user_id);
// Ofaj
$skills = $skill->getStudentSkills($this->user_id, 2);
$timeInSeconds = Tracking::get_time_spent_on_the_platform(
$this->user_id,
'ever'
);
$time = api_time_to_hms($timeInSeconds);
$tplContent = new Template(null, false, false, false, false, false);

@ -2169,9 +2169,7 @@ class CourseManager
return [];
}
$group_list = [];
$session_id != 0 ? $session_condition = ' WHERE g.session_id IN(1,'.intval($session_id).')' : $session_condition = ' WHERE g.session_id = 0';
if ($in_get_empty_group == 0) {
// get only groups that are not empty
$sql = "SELECT DISTINCT g.id, g.iid, g.name
@ -2187,14 +2185,15 @@ class CourseManager
$session_condition
AND c_id = $course_id";
}
$result = Database::query($sql);
while ($group_data = Database::fetch_array($result)) {
$group_data['userNb'] = GroupManager::number_of_students($group_data['id'], $course_id);
$group_list[$group_data['id']] = $group_data;
$result = Database::query($sql);
$groupList = [];
while ($groupData = Database::fetch_array($result)) {
$groupData['userNb'] = GroupManager::number_of_students($groupData['id'], $course_id);
$groupList[$groupData['iid']] = $groupData;
}
return $group_list;
return $groupList;
}
/**
@ -3011,12 +3010,12 @@ class CourseManager
/**
* Creates a new extra field for a given course.
*
* @param string Field's internal variable name
* @param int Field's type
* @param string Field's language var name
* @param string $default
* @param string $variable Field's internal variable name
* @param int $fieldType Field's type
* @param string $displayText Field's language var name
* @param string $default Optional. The default value
*
* @return bool new extra field id
* @return int New extra field ID
*/
public static function create_course_extra_field($variable, $fieldType, $displayText, $default = '')
{
@ -3084,11 +3083,11 @@ class CourseManager
/**
* Update an extra field value for a given course.
*
* @param int Course ID
* @param string Field variable name
* @param string Field value
* @param string $course_code Course code
* @param string $variable Field variable name
* @param string $value Optional. Default field value
*
* @return bool|null true if field updated, false otherwise
* @return bool|int An integer when register a new extra field. And boolean when update the extrafield
*/
public static function update_course_extra_field_value($course_code, $variable, $value = '')
{
@ -5980,38 +5979,38 @@ class CourseManager
*/
public static function getCourseGroups()
{
$session_id = api_get_session_id();
if ($session_id != 0) {
$new_group_list = self::get_group_list_of_course(
$sessionId = api_get_session_id();
if ($sessionId != 0) {
$groupList = self::get_group_list_of_course(
api_get_course_id(),
$session_id,
$sessionId,
1
);
} else {
$new_group_list = self::get_group_list_of_course(
$groupList = self::get_group_list_of_course(
api_get_course_id(),
0,
1
);
}
return $new_group_list;
return $groupList;
}
/**
* @param FormValidator $form
* @param array $to_already_selected
* @param array $alreadySelected
*
* @return HTML_QuickForm_element
*/
public static function addUserGroupMultiSelect(&$form, $to_already_selected)
public static function addUserGroupMultiSelect(&$form, $alreadySelected)
{
$userList = self::getCourseUsers(true);
$group_list = self::getCourseGroups();
$groupList = self::getCourseGroups();
$array = self::buildSelectOptions(
$group_list,
$groupList,
$userList,
$to_already_selected
$alreadySelected
);
$result = [];
@ -6090,38 +6089,39 @@ class CourseManager
/**
* this function shows the form for sending a message to a specific group or user.
*
* @param array $group_list
* @param array $groupList
* @param array $userList
* @param array $to_already_selected
* @param array $alreadySelected
*
* @return array
*/
public static function buildSelectOptions(
$group_list = [],
$groupList = [],
$userList = [],
$to_already_selected = []
$alreadySelected = []
) {
if (empty($to_already_selected)) {
$to_already_selected = [];
if (empty($alreadySelected)) {
$alreadySelected = [];
}
$result = [];
// adding the groups to the select form
if ($group_list) {
foreach ($group_list as $this_group) {
if (is_array($to_already_selected)) {
if ($groupList) {
foreach ($groupList as $thisGroup) {
$groupId = $thisGroup['iid'];
if (is_array($alreadySelected)) {
if (!in_array(
"GROUP:".$this_group['id'],
$to_already_selected
"GROUP:".$groupId,
$alreadySelected
)
) { // $to_already_selected is the array containing the groups (and users) that are already selected
$user_label = ($this_group['userNb'] > 0) ? get_lang('Users') : get_lang('LowerCaseUser');
$user_disabled = ($this_group['userNb'] > 0) ? "" : "disabled=disabled";
) { // $alreadySelected is the array containing the groups (and users) that are already selected
$user_label = ($thisGroup['userNb'] > 0) ? get_lang('Users') : get_lang('LowerCaseUser');
$user_disabled = ($thisGroup['userNb'] > 0) ? "" : "disabled=disabled";
$result[] = [
'disabled' => $user_disabled,
'value' => "GROUP:".$this_group['id'],
'value' => "GROUP:".$groupId,
// The space before "G" is needed in order to advmultiselect.php js puts groups first
'content' => " G: ".$this_group['name']." - ".$this_group['userNb']." ".$user_label,
'content' => " G: ".$thisGroup['name']." - ".$thisGroup['userNb']." ".$user_label,
];
}
}
@ -6131,12 +6131,13 @@ class CourseManager
// adding the individual users to the select form
if ($userList) {
foreach ($userList as $user) {
if (is_array($to_already_selected)) {
if (is_array($alreadySelected)) {
if (!in_array(
"USER:".$user['user_id'],
$to_already_selected
$alreadySelected
)
) { // $to_already_selected is the array containing the users (and groups) that are already selected
) {
// $alreadySelected is the array containing the users (and groups) that are already selected
$result[] = [
'value' => "USER:".$user['user_id'],
'content' => api_get_person_name($user['firstname'], $user['lastname']),

@ -177,12 +177,9 @@ class CourseCategory
}
// Now we're at the top, get back down to update each child
//$children_count = courseCategoryChildrenCount($categoryId);
$sql = "UPDATE $table SET children_count = (children_count - ".abs($delta).") WHERE code = '$categoryId'";
if ($delta >= 0) {
$sql = "UPDATE $table SET children_count = (children_count + $delta)
WHERE code = '$categoryId'";
} else {
$sql = "UPDATE $table SET children_count = (children_count - ".abs($delta).")
WHERE code = '$categoryId'";
$sql = "UPDATE $table SET children_count = (children_count + $delta) WHERE code = '$categoryId'";
}
Database::query($sql);
}

@ -251,9 +251,7 @@ class CourseHome
$html = '';
$web_code_path = api_get_path(WEB_CODE_PATH);
$course_tool_table = Database::get_course_table(TABLE_TOOL_LIST);
$course_id = api_get_course_int_id();
switch ($course_tool_category) {
case TOOL_PUBLIC:
$condition_display_tools = ' WHERE c_id = '.$course_id.' AND visibility = 1 ';
@ -571,13 +569,18 @@ class CourseHome
}
$allowEditionInSession = api_get_configuration_value('allow_edit_tool_visibility_in_session');
$toolWithSessionValue = [];
foreach ($tools as $row) {
if (!empty($row['session_id'])) {
$toolWithSessionValue[$row['name']] = $row;
// If exists same tool (by name) from session in base course then avoid it. Allow them pass in other cases
$tools = array_filter($tools, function (array $toolToFilter) use ($sessionId, $tools) {
if (!empty($toolToFilter['session_id'])) {
foreach ($tools as $originalTool) {
if ($toolToFilter['name'] == $originalTool['name'] && empty($originalTool['session_id'])) {
return false;
}
}
}
}
return true;
});
foreach ($tools as $temp_row) {
$add = false;
@ -589,13 +592,6 @@ class CourseHome
$add = true;
}
if (isset($toolWithSessionValue[$temp_row['name']])) {
if (!empty($temp_row['session_id'])) {
continue;
}
//$temp_row = $toolWithSessionValue[$temp_row['name']];
}
if ($allowEditionInSession && !empty($sessionId)) {
// Checking if exist row in session
$criteria = [
@ -1240,14 +1236,18 @@ class CourseHome
}
$navigation_items[$row['id']] = $row;
if (stripos($row['link'], 'http://') === false && stripos($row['link'], 'https://') === false) {
if (stripos($row['link'], 'http://') === false &&
stripos($row['link'], 'https://') === false
) {
$navigation_items[$row['id']]['link'] = api_get_path(WEB_CODE_PATH);
if ($row['category'] == 'plugin') {
$plugin = new AppPlugin();
$pluginInfo = $plugin->getPluginInfo($row['name']);
$navigation_items[$row['id']]['link'] = api_get_path(WEB_PLUGIN_PATH);
$navigation_items[$row['id']]['name'] = $pluginInfo['title'];
if (isset($pluginInfo['title'])) {
$navigation_items[$row['id']]['link'] = api_get_path(WEB_PLUGIN_PATH);
$navigation_items[$row['id']]['name'] = $pluginInfo['title'];
}
} else {
$navigation_items[$row['id']]['name'] = self::translate_tool_name($row);
}

@ -1,10 +1,12 @@
<?php
/* For licensing terms, see /license.txt */
use Doctrine\Common\Annotations\AnnotationRegistry;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Driver\Statement;
use Doctrine\DBAL\Types\Type;
use Doctrine\ORM\EntityManager;
use Symfony\Component\Debug\ExceptionHandler;
/**
* Class Database.

@ -850,7 +850,6 @@ class Display
// When moving this to production, the return_icon() calls should
// ask for the SVG version directly
$svgIcons = api_get_setting('icons_mode_svg');
if ($svgIcons == 'true' && $return_only_path == false) {
$svgImage = substr($image, 0, -3).'svg';
if (is_file($code_path.$theme.'svg/'.$svgImage)) {
@ -1044,6 +1043,15 @@ class Display
/**
* Displays an HTML select tag.
*
* @param string $name
* @param array $values
* @param int $default
* @param array $extra_attributes
* @param bool $show_blank_item
* @param null $blank_item_text
*
* @return string
*/
public static function select(
$name,
@ -1051,7 +1059,7 @@ class Display
$default = -1,
$extra_attributes = [],
$show_blank_item = true,
$blank_item_text = null
$blank_item_text = ''
) {
$html = '';
$extra = '';
@ -2478,7 +2486,7 @@ class Display
$style = !in_array($type, $typeList) ? 'default' : $type;
if (!empty($id)) {
$id = " id = $id ";
$id = " id='$id'";
}
return '

@ -296,12 +296,15 @@ class DocumentManager
if (!is_file($full_file_name)) {
return false;
}
$filename = ($name == '') ? basename($full_file_name) : api_replace_dangerous_char($name);
$filename = $name == '' ? basename($full_file_name) : api_replace_dangerous_char($name);
$len = filesize($full_file_name);
// Fixing error when file name contains a ","
$filename = str_replace(',', '', $filename);
$sendFileHeaders = api_get_configuration_value('enable_x_sendfile_headers');
// Allows chrome to make videos and audios seekable
header('Accept-Ranges: bytes');
if ($forced) {
// Force the browser to save the file instead of opening it
if (isset($sendFileHeaders) &&
@ -325,7 +328,8 @@ class DocumentManager
header('Content-Transfer-Encoding: binary');
if (function_exists('ob_end_clean')) {
// Use ob_end_clean() to avoid weird buffering situations where file is sent broken/incomplete for download
// Use ob_end_clean() to avoid weird buffering situations
// where file is sent broken/incomplete for download
ob_end_clean();
}
@ -334,8 +338,7 @@ class DocumentManager
return true;
} else {
//no forced download, just let the browser decide what to do according to the mimetype
$content_type = self::file_get_mime_type($filename);
// no forced download, just let the browser decide what to do according to the mimetype
$lpFixedEncoding = api_get_configuration_value('lp_fixed_encoding');
// Commented to let courses content to be cached in order to improve performance:
@ -345,24 +348,25 @@ class DocumentManager
// Commented to avoid double caching declaration when playing with IE and HTTPS
//header('Cache-Control: no-cache, must-revalidate');
//header('Pragma: no-cache');
switch ($content_type) {
$contentType = self::file_get_mime_type($filename);
switch ($contentType) {
case 'text/html':
if (isset($lpFixedEncoding) && $lpFixedEncoding === 'true') {
$content_type .= '; charset=UTF-8';
$contentType .= '; charset=UTF-8';
} else {
$encoding = @api_detect_encoding_html(file_get_contents($full_file_name));
if (!empty($encoding)) {
$content_type .= '; charset='.$encoding;
$contentType .= '; charset='.$encoding;
}
}
break;
case 'text/plain':
if (isset($lpFixedEncoding) && $lpFixedEncoding === 'true') {
$content_type .= '; charset=UTF-8';
$contentType .= '; charset=UTF-8';
} else {
$encoding = @api_detect_encoding(strip_tags(file_get_contents($full_file_name)));
if (!empty($encoding)) {
$content_type .= '; charset='.$encoding;
$contentType .= '; charset='.$encoding;
}
}
break;
@ -371,7 +375,7 @@ class DocumentManager
header('Content-type: application/octet-stream');
break;
}
header('Content-type: '.$content_type);
header('Content-type: '.$contentType);
header('Content-Length: '.$len);
$user_agent = strtolower($_SERVER['HTTP_USER_AGENT']);
if (strpos($user_agent, 'msie')) {
@ -390,7 +394,8 @@ class DocumentManager
echo $content;
} else {
if (function_exists('ob_end_clean')) {
// Use ob_end_clean() to avoid weird buffering situations where file is sent broken/incomplete for download
// Use ob_end_clean() to avoid weird buffering situations
// where file is sent broken/incomplete for download
ob_end_clean();
}
@ -429,39 +434,39 @@ class DocumentManager
/**
* Fetches all document data for the given user/group.
*
* @param array $_course
* @param array $courseInfo
* @param string $path
* @param int $to_group_id iid
* @param int $to_user_id
* @param bool $can_see_invisible
* @param int $toGroupId iid
* @param int $toUserId
* @param bool $canSeeInvisible
* @param bool $search
* @param int $sessionId
*
* @return array with all document data
*/
public static function get_all_document_data(
$_course,
public static function getAllDocumentData(
$courseInfo,
$path = '/',
$to_group_id = 0,
$to_user_id = null,
$can_see_invisible = false,
$toGroupId = 0,
$toUserId = null,
$canSeeInvisible = false,
$search = false,
$sessionId = 0
) {
$TABLE_ITEMPROPERTY = Database::get_course_table(TABLE_ITEM_PROPERTY);
$TABLE_DOCUMENT = Database::get_course_table(TABLE_DOCUMENT);
$tblItemProperty = Database::get_course_table(TABLE_ITEM_PROPERTY);
$tblDocument = Database::get_course_table(TABLE_DOCUMENT);
$userGroupFilter = '';
if (!is_null($to_user_id)) {
$to_user_id = intval($to_user_id);
$userGroupFilter = "last.to_user_id = $to_user_id";
if (empty($to_user_id)) {
if (!is_null($toUserId)) {
$toUserId = intval($toUserId);
$userGroupFilter = "last.to_user_id = $toUserId";
if (empty($toUserId)) {
$userGroupFilter = " (last.to_user_id = 0 OR last.to_user_id IS NULL) ";
}
} else {
$to_group_id = intval($to_group_id);
$userGroupFilter = "last.to_group_id = $to_group_id";
if (empty($to_group_id)) {
$toGroupId = intval($toGroupId);
$userGroupFilter = "last.to_group_id = $toGroupId";
if (empty($toGroupId)) {
$userGroupFilter = "( last.to_group_id = 0 OR last.to_group_id IS NULL) ";
}
}
@ -470,23 +475,23 @@ class DocumentManager
$originalPath = $path;
$path = str_replace('_', '\_', $path);
$visibility_bit = ' <> 2';
$visibilityBit = ' <> 2';
// The given path will not end with a slash, unless it's the root '/'
// so no root -> add slash
$added_slash = $path == '/' ? '' : '/';
$addedSlash = $path == '/' ? '' : '/';
// Condition for the session
$sessionId = $sessionId ?: api_get_session_id();
$condition_session = " AND (last.session_id = '$sessionId' OR (last.session_id = '0' OR last.session_id IS NULL) )";
$condition_session .= self::getSessionFolderFilters($originalPath, $sessionId);
$conditionSession = " AND (last.session_id = '$sessionId' OR (last.session_id = '0' OR last.session_id IS NULL) )";
$conditionSession .= self::getSessionFolderFilters($originalPath, $sessionId);
$sharedCondition = null;
if ($originalPath == '/shared_folder') {
$students = CourseManager::get_user_list_from_course_code($_course['code'], $sessionId);
$students = CourseManager::get_user_list_from_course_code($courseInfo['code'], $sessionId);
if (!empty($students)) {
$conditionList = [];
foreach ($students as $studentId => $studentInfo) {
foreach ($students as $studentInfo) {
$conditionList[] = '/shared_folder/sf_user_'.$studentInfo['user_id'];
}
$sharedCondition .= ' AND docs.path IN ("'.implode('","', $conditionList).'")';
@ -506,136 +511,106 @@ class DocumentManager
last.lastedit_date,
last.visibility,
last.insert_user_id
FROM $TABLE_ITEMPROPERTY AS last
INNER JOIN $TABLE_DOCUMENT AS docs
FROM $tblItemProperty AS last
INNER JOIN $tblDocument AS docs
ON (
docs.id = last.ref AND
docs.c_id = last.c_id
)
WHERE
last.tool = '".TOOL_DOCUMENT."' AND
docs.c_id = {$_course['real_id']} AND
last.c_id = {$_course['real_id']} AND
docs.path LIKE '".Database::escape_string($path.$added_slash.'%')."' AND
docs.path NOT LIKE '".Database::escape_string($path.$added_slash.'%/%')."' AND
docs.c_id = {$courseInfo['real_id']} AND
last.c_id = {$courseInfo['real_id']} AND
docs.path LIKE '".Database::escape_string($path.$addedSlash.'%')."' AND
docs.path NOT LIKE '".Database::escape_string($path.$addedSlash.'%/%')."' AND
docs.path NOT LIKE '%_DELETED_%' AND
$userGroupFilter AND
last.visibility $visibility_bit
$condition_session
last.visibility $visibilityBit
$conditionSession
$sharedCondition
ORDER BY last.iid DESC, last.session_id DESC
";
$result = Database::query($sql);
$doc_list = [];
$document_data = [];
$is_allowed_to_edit = api_is_allowed_to_edit(null, true);
$documentData = [];
$isAllowedToEdit = api_is_allowed_to_edit(null, true);
$isCoach = api_is_coach();
if ($result !== false && Database::num_rows($result) != 0) {
$rows = [];
$hideInvisibleDocuments = api_get_configuration_value('hide_invisible_course_documents_in_sessions');
while ($row = Database::fetch_array($result, 'ASSOC')) {
if ($isCoach) {
// Looking for course items that are invisible to hide it in the session
if (in_array($row['id'], array_keys($doc_list))) {
if ($doc_list[$row['id']]['item_property_session_id'] == 0 &&
$doc_list[$row['id']]['session_id'] == 0
) {
if ($doc_list[$row['id']]['visibility'] == 0) {
unset($document_data[$row['id']]);
continue;
}
}
}
$doc_list[$row['id']] = $row;
if (isset($rows[$row['id']])) {
continue;
}
if (!$isCoach && !$is_allowed_to_edit) {
$doc_list[] = $row;
// If we are in session and hide_invisible_course_documents_in_sessions is enabled
// Then we avoid the documents that have visibility in session but that they come from a base course
if ($hideInvisibleDocuments && $sessionId) {
if ($row['item_property_session_id'] == $sessionId && empty($row['session_id'])) {
continue;
}
}
$rows[$row['id']] = $row;
}
// If we are in session and hide_invisible_course_documents_in_sessions is enabled
// Or if we are students
// Then don't list the invisible or deleted documents
if (($sessionId && $hideInvisibleDocuments) || (!$isCoach && !$isAllowedToEdit)) {
$rows = array_filter($rows, function ($row) {
if (in_array($row['visibility'], ['0', '2'])) {
return false;
}
return true;
});
}
foreach ($rows as $row) {
if ($row['filetype'] == 'file' &&
pathinfo($row['path'], PATHINFO_EXTENSION) == 'html'
) {
// Templates management
$table_template = Database::get_main_table(TABLE_MAIN_TEMPLATES);
$sql = "SELECT id FROM $table_template
$tblTemplate = Database::get_main_table(TABLE_MAIN_TEMPLATES);
$sql = "SELECT id FROM $tblTemplate
WHERE
course_code = '".$_course['code']."' AND
course_code = '".$courseInfo['code']."' AND
user_id = '".api_get_user_id()."' AND
ref_doc = '".$row['id']."'";
$template_result = Database::query($sql);
$row['is_template'] = (Database::num_rows($template_result) > 0) ? 1 : 0;
$templateResult = Database::query($sql);
$row['is_template'] = (Database::num_rows($templateResult) > 0) ? 1 : 0;
}
$row['basename'] = basename($row['path']);
// Just filling $document_data.
$document_data[$row['id']] = $row;
$documentData[$row['id']] = $row;
}
// Only for the student we filter the results see BT#1652
if (!$isCoach && !$is_allowed_to_edit) {
$ids_to_remove = [];
$my_repeat_ids = $temp = [];
// Selecting repeated ids
foreach ($doc_list as $row) {
if (in_array($row['id'], array_keys($temp))) {
$my_repeat_ids[] = $row['id'];
}
$temp[$row['id']] = $row;
}
//@todo use the self::is_visible function
// Checking visibility in a session
foreach ($my_repeat_ids as $id) {
foreach ($doc_list as $row) {
if ($id == $row['id']) {
if ($row['visibility'] == 0 && $row['item_property_session_id'] == 0) {
$delete_repeated[$id] = true;
}
if ($row['visibility'] == 0 && $row['item_property_session_id'] != 0) {
$delete_repeated[$id] = true;
}
}
}
}
foreach ($doc_list as $key => $row) {
if (in_array($row['visibility'], ['0', '2']) &&
!in_array($row['id'], $my_repeat_ids)
) {
$ids_to_remove[] = $row['id'];
unset($doc_list[$key]);
}
}
foreach ($document_data as $row) {
if (in_array($row['id'], $ids_to_remove)) {
unset($document_data[$row['id']]);
}
if (isset($delete_repeated[$row['id']]) && $delete_repeated[$row['id']]) {
unset($document_data[$row['id']]);
}
}
if (!$isCoach && !$isAllowedToEdit) {
// Checking parents visibility.
$final_document_data = [];
foreach ($document_data as $row) {
$is_visible = self::check_visibility_tree(
$finalDocumentData = [];
foreach ($documentData as $row) {
$isVisible = self::check_visibility_tree(
$row['id'],
$_course['code'],
$courseInfo['code'],
$sessionId,
api_get_user_id(),
$to_group_id
$toGroupId
);
if ($is_visible) {
$final_document_data[$row['id']] = $row;
if ($isVisible) {
$finalDocumentData[$row['id']] = $row;
}
}
} else {
$final_document_data = $document_data;
$finalDocumentData = $documentData;
}
return $final_document_data;
return $finalDocumentData;
} else {
return false;
return [];
}
}
@ -871,7 +846,7 @@ class DocumentManager
$_course,
$user_id,
$file = null,
$document_id = '',
$document_id = 0,
$to_delete = false,
$sessionId = null,
$documentId = null
@ -1059,16 +1034,14 @@ class DocumentManager
) {
$TABLE_DOCUMENT = Database::get_course_table(TABLE_DOCUMENT);
$groupId = intval($groupId);
if (empty($groupId)) {
$groupId = api_get_group_id();
} else {
$groupId = intval($groupId);
}
$sessionId = intval($sessionId);
if (empty($sessionId)) {
$sessionId = api_get_session_id();
} else {
$sessionId = intval($sessionId);
}
$course_id = $_course['real_id'];
@ -1466,7 +1439,6 @@ class DocumentManager
$course_code = Database::escape_string($course_code);
$user_id = intval($user_id);
$document_id = intval($document_id);
$sql = 'SELECT id FROM '.$table_template.'
WHERE
course_code="'.$course_code.'" AND
@ -1507,7 +1479,8 @@ class DocumentManager
$propTable = Database::get_course_table(TABLE_ITEM_PROPERTY);
$course_id = $course['real_id'];
//note the extra / at the end of doc_path to match every path in the document table that is part of the document path
// note the extra / at the end of doc_path to match every path in
// the document table that is part of the document path
$session_id = intval($session_id);
$condition = "AND d.session_id IN ('$session_id', '0') ";
@ -1716,7 +1689,7 @@ class DocumentManager
* @param int $document_id
* @param int $session_id
*/
public static function attach_gradebook_certificate($courseCode, $document_id, $session_id = 0)
public static function attach_gradebook_certificate($course_id, $document_id, $session_id = 0)
{
$tbl_category = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
$session_id = intval($session_id);
@ -1731,10 +1704,8 @@ class DocumentManager
} else {
$sql_session = '';
}
$courseInfo = api_get_course_info($courseCode);
$sql = 'UPDATE '.$tbl_category.' SET document_id="'.intval($document_id).'"
WHERE c_id ="'.$courseInfo['real_id'].'" '.$sql_session;
WHERE course_code="'.Database::escape_string($course_id).'" '.$sql_session;
Database::query($sql);
}
@ -1748,7 +1719,7 @@ class DocumentManager
*
* @return int The default certificate id
*/
public static function get_default_certificate_id($courseCode, $session_id = 0)
public static function get_default_certificate_id($course_id, $session_id = 0)
{
$tbl_category = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
$session_id = intval($session_id);
@ -1763,9 +1734,8 @@ class DocumentManager
} else {
$sql_session = '';
}
$courseInfo = api_get_course_info($courseCode);
$sql = 'SELECT document_id FROM '.$tbl_category.'
WHERE c_id ="'.$courseInfo['real_id'].'" '.$sql_session;
WHERE course_code="'.Database::escape_string($course_id).'" '.$sql_session;
$rs = Database::query($sql);
$num = Database::num_rows($rs);
@ -1785,7 +1755,7 @@ class DocumentManager
* @param int $sessionId
* @param bool $is_preview
*
* @return string The html content of the certificate
* @return array
*/
public static function replace_user_info_into_html(
$user_id,
@ -2005,7 +1975,6 @@ class DocumentManager
$course_dir = $courseInfo['path']."/document/";
$sys_course_path = api_get_path(SYS_COURSE_PATH);
$base_work_dir = $sys_course_path.$course_dir;
$base_work_dir_test = $base_work_dir.'certificates';
$dir_name = '/certificates';
$post_dir_name = get_lang('CertificatesFiles');
$visibility_command = 'invisible';
@ -2157,8 +2126,12 @@ class DocumentManager
case 'shtml':
case 'css':
$file_content = file_get_contents($abs_path);
//get an array of attributes from the HTML source
$attributes = self::parse_HTML_attributes($file_content, $wanted_attributes, $explode_attributes);
// get an array of attributes from the HTML source
$attributes = self::parse_HTML_attributes(
$file_content,
$wanted_attributes,
$explode_attributes
);
break;
default:
break;
@ -2476,13 +2449,13 @@ class DocumentManager
/**
* Parses the HTML attributes given as string.
*
* @param string HTML attribute string
* @param array List of attributes that we want to get back
* @param array
* @param string HTML attribute string
* @param array List of attributes that we want to get back
* @param array
*
* @return array An associative array of attributes
*
* @author Based on a function from the HTML_Common2 PEAR module *
* @author Based on a function from the HTML_Common2 PEAR module *
*/
public static function parse_HTML_attributes($attrString, $wanted = [], $explode_variables = [])
{
@ -2649,7 +2622,11 @@ class DocumentManager
$perm = api_get_permissions_for_new_directories();
$result = @mkdir($filepath_dir, $perm, true);
if ($result) {
$filepath_to_add = str_replace([$dest_course_path, 'document'], '', $filepath_dir);
$filepath_to_add = str_replace(
[$dest_course_path, 'document'],
'',
$filepath_dir
);
//Add to item properties to the new folder
$doc_id = add_document(
@ -2676,7 +2653,11 @@ class DocumentManager
if (!file_exists($destination_filepath)) {
$result = @copy($origin_filepath, $destination_filepath);
if ($result) {
$filepath_to_add = str_replace([$dest_course_path, 'document'], '', $destination_filepath);
$filepath_to_add = str_replace(
[$dest_course_path, 'document'],
'',
$destination_filepath
);
$size = filesize($destination_filepath);
// Add to item properties to the file
@ -2704,12 +2685,13 @@ class DocumentManager
// Replace origin course path by destination course path.
if (strpos($content_html, $real_orig_url) !== false) {
$url_course_path = str_replace($orig_course_info_path.'/'.$document_file, '', $real_orig_path);
//$destination_url = $url_course_path . $destination_course_directory . '/' . $document_file . $dest_url_query;
$url_course_path = str_replace(
$orig_course_info_path.'/'.$document_file,
'',
$real_orig_path
);
// See BT#7780
$destination_url = $dest_course_path_rel.$document_file.$dest_url_query;
// If the course code doesn't exist in the path? what we do? Nothing! see BT#1985
if (strpos($real_orig_path, $origin_course_code) === false) {
$url_course_path = $real_orig_path;
@ -2788,7 +2770,7 @@ class DocumentManager
* @param string $title
* @param string $comment
* @param int $unzip unzip or not the file
* @param string $if_exists overwrite, rename or warn (default)
* @param string $ifExists overwrite, rename or warn (default)
* @param bool $index_document index document (search xapian module)
* @param bool $show_output print html messages
* @param string $fileKey
@ -2799,10 +2781,10 @@ class DocumentManager
public static function upload_document(
$files,
$path,
$title = null,
$comment = null,
$title = '',
$comment = '',
$unzip = 0,
$if_exists = null,
$ifExists = '',
$index_document = false,
$show_output = false,
$fileKey = 'file',
@ -2815,9 +2797,8 @@ class DocumentManager
$base_work_dir = $sys_course_path.$course_dir;
if (isset($files[$fileKey])) {
$upload_ok = process_uploaded_file($files[$fileKey], $show_output);
if ($upload_ok) {
$uploadOk = process_uploaded_file($files[$fileKey], $show_output);
if ($uploadOk) {
$new_path = handle_uploaded_document(
$course_info,
$files[$fileKey],
@ -2827,7 +2808,7 @@ class DocumentManager
api_get_group_id(),
null,
$unzip,
$if_exists,
$ifExists,
$show_output,
false,
null,
@ -2846,8 +2827,8 @@ class DocumentManager
}
return [
'title' => $path,
'url' => $path,
'title' => $files[$fileKey]['name'],
'url' => '#',
];
}
@ -2889,7 +2870,7 @@ class DocumentManager
null,
$_POST['language'],
$_REQUEST,
$if_exists
$ifExists
);
}
@ -3011,12 +2992,12 @@ class DocumentManager
$course_id = api_get_course_int_id();
}
$group_condition = null;
$group_condition = '';
if ($group_id) {
$group_condition = " AND props.to_group_id='".$group_id."' ";
}
$session_condition = null;
$session_condition = '';
if ($session_id) {
$session_condition = " AND props.session_id='".$session_id."' ";
}
@ -3155,7 +3136,7 @@ class DocumentManager
</div>
</div>
</div>';
//<div id="jplayer_inspector_'.$i.'"></div>
return $html;
}
@ -3166,7 +3147,6 @@ class DocumentManager
*/
public static function generate_video_preview($document_data = [])
{
//<button class="jp-video-play-icon" role="button" tabindex="0">play</button>
$html = '
<div id="jp_container_1" class="jp-video center-block" role="application" aria-label="media player">
<div class="jp-type-single">
@ -4048,12 +4028,7 @@ class DocumentManager
$formatTypesList = [];
$formatTypes = ['text', 'spreadsheet', 'presentation', 'drawing'];
foreach ($formatTypes as $formatType) {
if (
in_array(
$extension,
self::getJodconverterExtensionList($mode, $formatType)
)
) {
if (in_array($extension, self::getJodconverterExtensionList($mode, $formatType))) {
$formatTypesList[] = $formatType;
}
}
@ -5422,11 +5397,11 @@ class DocumentManager
/**
* Creates the row of edit icons for a file/folder.
*
* @param string $curdirpath current path (cfr open folder)
* @param string $type (file/folder)
* @param string $path dbase path of file/folder
* @param int $visibility (1/0)
* @param int $id dbase id of the document
* @param array $document_data
* @param int $id
* @param bool $is_template
* @param int $is_read_only
* @param int $visibility (1/0)
*
* @return string html img tags with hyperlinks
*/
@ -5565,7 +5540,7 @@ class DocumentManager
$form = new FormValidator('move_to', 'post', api_get_self().'?'.api_get_cidreq());
// Form title
$form->addElement('hidden', 'move_file', $move_file);
$form->addHidden('move_file', $move_file);
$options = [];
@ -5734,16 +5709,16 @@ class DocumentManager
* Checks whether the user is into any user shared folder.
*
* @param string $path
* @param int $current_session_id
* @param int $sessionId
*
* @return bool Return true when user is in any user shared folder
*/
public static function is_any_user_shared_folder($path, $current_session_id)
public static function is_any_user_shared_folder($path, $sessionId)
{
$clean_path = Security::remove_XSS($path);
if (strpos($clean_path, 'shared_folder/sf_user_')) {
return true;
} elseif (strpos($clean_path, 'shared_folder_session_'.$current_session_id.'/sf_user_')) {
} elseif (strpos($clean_path, 'shared_folder_session_'.$sessionId.'/sf_user_')) {
return true;
} else {
return false;
@ -5830,17 +5805,17 @@ class DocumentManager
*
* @param int $user_id
* @param string $path
* @param int $current_session_id
* @param int $sessionId
*
* @return bool Return true when user is in his user shared folder or into a subfolder
*/
public static function is_my_shared_folder($user_id, $path, $current_session_id)
public static function is_my_shared_folder($user_id, $path, $sessionId)
{
$clean_path = Security::remove_XSS($path).'/';
//for security does not remove the last slash
$main_user_shared_folder = '/shared_folder\/sf_user_'.$user_id.'\//';
//for security does not remove the last slash
$main_user_shared_folder_session = '/shared_folder_session_'.$current_session_id.'\/sf_user_'.$user_id.'\//';
$main_user_shared_folder_session = '/shared_folder_session_'.$sessionId.'\/sf_user_'.$user_id.'\//';
if (preg_match($main_user_shared_folder, $clean_path)) {
return true;
@ -5885,7 +5860,7 @@ class DocumentManager
/*
//TODO: make a admin switch to strict mode
1. global default $allowed_extensions only: 'htm', 'html', 'xhtml', 'gif', 'jpg', 'jpeg', 'png', 'bmp', 'txt', 'log'
1. global default $allowed_extensions
if (in_array($file_extension, $allowed_extensions)) { // Assignment + a logical check.
return true;
}
@ -5893,7 +5868,8 @@ class DocumentManager
3. check plugins: quicktime, mediaplayer, vlc, acrobat, flash, java
*/
if (!($result = in_array($file_extension, $allowed_extensions))) { // Assignment + a logical check.
if (!($result = in_array($file_extension, $allowed_extensions))) {
// Assignment + a logical check.
return false;
}
@ -6185,7 +6161,9 @@ class DocumentManager
$new_path = Database::escape_string($new_path);
$query = "UPDATE $dbTable SET
path = CONCAT('".$new_path."', SUBSTRING(path, LENGTH('".$old_path."')+1) )
WHERE c_id = $course_id AND (path LIKE BINARY '".$old_path."' OR path LIKE BINARY '".$old_path."/%')";
WHERE
c_id = $course_id AND
(path LIKE BINARY '".$old_path."' OR path LIKE BINARY '".$old_path."/%')";
Database::query($query);
break;
}
@ -6556,7 +6534,6 @@ class DocumentManager
}
$return .= '<div class="item_data" style="margin-left:'.($num * 5).'px;margin-right:5px;">';
if ($add_move_button) {
$return .= '<a class="moved" href="#">';
$return .= Display::return_icon('move_everywhere.png', get_lang('Move'), [], ICON_SIZE_TINY);
@ -6604,22 +6581,7 @@ class DocumentManager
return null;
}
//trad some titles
/*
if ($key == 'images') {
$key = get_lang('Images');
} elseif ($key == 'gallery') {
$key = get_lang('Gallery');
} elseif ($key == 'flash') {
$key = get_lang('Flash');
} elseif ($key == 'audio') {
$key = get_lang('Audio');
} elseif ($key == 'video') {
$key = get_lang('Video');
}*/
$onclick = '';
// if in LP, hidden folder are displayed in grey
$folder_class_hidden = '';
if ($lp_id) {

@ -82,7 +82,7 @@ class Event
Database::query($sql);
// Auto subscribe
$user_status = $userInfo['status'] == SESSIONADMIN ? 'sessionadmin' : $userInfo['status'] == COURSEMANAGER ? 'teacher' : $userInfo['status'] == DRH ? 'drh' : 'student';
$user_status = $userInfo['status'] == SESSIONADMIN ? 'sessionadmin' : $userInfo['status'] == COURSEMANAGER ? 'teacher' : $userInfo['status'] == DRH ? 'DRH' : 'student';
$autoSubscribe = api_get_setting($user_status.'_autosubscribe');
if ($autoSubscribe) {
$autoSubscribe = explode('|', $autoSubscribe);

@ -39,13 +39,13 @@ class ExerciseLib
$freeze = false,
$user_choice = [],
$show_comment = false,
$show_answers = false
$show_answers = false,
$show_icon = false
) {
$course_id = empty($exercise->course_id) ? api_get_course_int_id() : $exercise->course_id;
$course = api_get_course_info_by_id($course_id);
// Change false to true in the following line to enable answer hinting
$debug_mark_answer = $show_answers;
// Reads question information
if (!$objQuestionTmp = Question::read($questionId)) {
// Question not found
@ -70,7 +70,7 @@ class ExerciseLib
if ($exercise->display_category_name) {
TestCategory::displayCategoryAndTitle($objQuestionTmp->id);
}
$titleToDisplay = null;
$titleToDisplay = $objQuestionTmp->getTitleToDisplay($current_item);
if ($answerType == READING_COMPREHENSION) {
// In READING_COMPREHENSION, the title of the question
// contains the question itself, which can only be
@ -79,8 +79,6 @@ class ExerciseLib
$current_item.'. '.get_lang('ReadingComprehension'),
['class' => 'question_title']
);
} else {
$titleToDisplay = $objQuestionTmp->getTitleToDisplay($current_item);
}
echo $titleToDisplay;
}
@ -92,9 +90,7 @@ class ExerciseLib
}
}
if (in_array($answerType, [FREE_ANSWER, ORAL_EXPRESSION]) &&
$freeze
) {
if (in_array($answerType, [FREE_ANSWER, ORAL_EXPRESSION]) && $freeze) {
return '';
}
@ -191,7 +187,6 @@ class ExerciseLib
if (api_get_setting('enable_record_audio') == 'true') {
//@todo pass this as a parameter
global $exercise_stat_info, $exerciseId;
if (!empty($exercise_stat_info)) {
$objQuestionTmp->initFile(
api_get_session_id(),
@ -290,9 +285,7 @@ class ExerciseLib
$hidingClass = '';
if ($answerType == READING_COMPREHENSION) {
$objQuestionTmp->processText(
$objQuestionTmp->selectDescription()
);
$objQuestionTmp->processText($objQuestionTmp->selectDescription());
$hidingClass = 'hide-reading-answers';
$s .= Display::div(
$objQuestionTmp->selectTitle(),
@ -337,15 +330,17 @@ class ExerciseLib
if ($answerType == UNIQUE_ANSWER_IMAGE) {
if ($show_comment) {
if (empty($comment)) {
$s .= '<div id="answer'.$questionId.$numAnswer.'" '
.'class="exercise-unique-answer-image" style="text-align: center">';
$s .= '<div id="answer'.$questionId.$numAnswer.'"
class="exercise-unique-answer-image" style="text-align: center">';
} else {
$s .= '<div id="answer'.$questionId.$numAnswer.'" '
.'class="exercise-unique-answer-image col-xs-6 col-sm-12" style="text-align: center">';
$s .= '<div id="answer'.$questionId.$numAnswer.'"
class="exercise-unique-answer-image col-xs-6 col-sm-12"
style="text-align: center">';
}
} else {
$s .= '<div id="answer'.$questionId.$numAnswer.'" '
.'class="exercise-unique-answer-image col-xs-6 col-md-3" style="text-align: center">';
$s .= '<div id="answer'.$questionId.$numAnswer.'"
class="exercise-unique-answer-image col-xs-6 col-md-3"
style="text-align: center">';
}
}
@ -585,10 +580,8 @@ class ExerciseLib
// or filled to display the answer in the Question preview of the exercise/admin.php page
$displayForStudent = true;
$listAnswerInfo = FillBlanks::getAnswerInfo($answer);
// Correct answers
$correctAnswerList = $listAnswerInfo['words'];
// Student's answer
$studentAnswerList = [];
if (isset($user_choice[0]['answer'])) {
@ -599,8 +592,8 @@ class ExerciseLib
$studentAnswerList = $arrayStudentAnswer['student_answer'];
}
// If the question must be shown with the answer (in page exercise/admin.php) for teacher preview
// set the student-answer to the correct answer
// If the question must be shown with the answer (in page exercise/admin.php)
// for teacher preview set the student-answer to the correct answer
if ($debug_mark_answer) {
$studentAnswerList = $correctAnswerList;
$displayForStudent = false;
@ -675,13 +668,11 @@ class ExerciseLib
$rowLastAttempt = Database::fetch_array($rsLastAttempt);
$answer = $rowLastAttempt['answer'];
if (empty($answer)) {
$_SESSION['calculatedAnswerId'][$questionId] = mt_rand(
1,
$nbrAnswers
);
$answer = $objAnswerTmp->selectAnswer(
$_SESSION['calculatedAnswerId'][$questionId]
);
$randomValue = mt_rand(1, $nbrAnswers);
$answer = $objAnswerTmp->selectAnswer($randomValue);
$calculatedAnswer = Session::read('calculatedAnswerId');
$calculatedAnswer[$questionId] = $randomValue;
Session::write('calculatedAnswerId', $calculatedAnswer);
}
}
@ -693,7 +684,8 @@ class ExerciseLib
$correctAnswerList
);
// get student answer to display it if student go back to previous calculated answer question in a test
// get student answer to display it if student go back to
// previous calculated answer question in a test
if (isset($user_choice[0]['answer'])) {
api_preg_match_all(
'/\[[^]]+\]/',
@ -703,9 +695,7 @@ class ExerciseLib
$studentAnswerListTobecleaned = $studentAnswerList[0];
$studentAnswerList = [];
for ($i = 0; $i < count(
$studentAnswerListTobecleaned
); $i++) {
for ($i = 0; $i < count($studentAnswerListTobecleaned); $i++) {
$answerCorrected = $studentAnswerListTobecleaned[$i];
$answerCorrected = api_preg_replace(
'| / <font color="green"><b>.*$|',
@ -732,7 +722,8 @@ class ExerciseLib
}
}
// If display preview of answer in test view for exemple, set the student answer to the correct answers
// If display preview of answer in test view for exaemple,
// set the student answer to the correct answers
if ($debug_mark_answer) {
// contain the rights answers surronded with brackets
$studentAnswerList = $correctAnswerList[0];
@ -751,7 +742,7 @@ class ExerciseLib
' '.$answer.' '
);
if (!empty($correctAnswerList) && !empty($studentAnswerList)) {
$answer = "";
$answer = '';
$i = 0;
foreach ($studentAnswerList as $studentItem) {
// remove surronding brackets
@ -816,7 +807,9 @@ class ExerciseLib
// Id of select is # question + # of option
$s .= '<td width="10%" valign="top" align="center">
<div class="select-matching">
<select id="choice_id_'.$current_item.'_'.$lines_count.'" name="choice['.$questionId.']['.$numAnswer.']">';
<select
id="choice_id_'.$current_item.'_'.$lines_count.'"
name="choice['.$questionId.']['.$numAnswer.']">';
// fills the list-box
foreach ($select_items as $key => $val) {
@ -840,7 +833,12 @@ class ExerciseLib
$s .= '</select></div></td><td width="5%" class="separate">&nbsp;</td>';
$s .= '<td width="40%" valign="top" >';
if (isset($select_items[$lines_count])) {
$s .= '<div class="text-right"><p class="indent">'.$select_items[$lines_count]['letter'].'.&nbsp; '.$select_items[$lines_count]['answer'].'</p></div>';
$s .= '<div class="text-right">
<p class="indent">'.
$select_items[$lines_count]['letter'].'.&nbsp; '.
$select_items[$lines_count]['answer'].'
</p>
</div>';
} else {
$s .= '&nbsp;';
}
@ -855,7 +853,8 @@ class ExerciseLib
$s .= '<tr>
<td colspan="2"></td>
<td valign="top">';
$s .= '<b>'.$select_items[$lines_count]['letter'].'.</b> '.$select_items[$lines_count]['answer'];
$s .= '<b>'.$select_items[$lines_count]['letter'].'.</b> '.
$select_items[$lines_count]['answer'];
$s .= "</td>
</tr>";
$lines_count++;
@ -962,7 +961,8 @@ class ExerciseLib
$s .= <<<HTML
<tr>
<td width="45%">
<div id="window_{$windowId}" class="window window_left_question window{$questionId}_question">
<div id="window_{$windowId}"
class="window window_left_question window{$questionId}_question">
<strong>$lines_count.</strong>
$answer
</div>
@ -1034,7 +1034,8 @@ HTML;
if (isset($select_items[$lines_count])) {
$s .= <<<HTML
<div id="window_{$windowId}_answer" class="window window_right_question">
<strong>{$select_items[$lines_count]['letter']}.</strong> {$select_items[$lines_count]['answer']}
<strong>{$select_items[$lines_count]['letter']}.</strong>
{$select_items[$lines_count]['answer']}
</div>
HTML;
} else {
@ -1142,7 +1143,6 @@ HTML;
}
}
}
$questionName = $objQuestionTmp->selectTitle();
$questionDescription = $objQuestionTmp->selectDescription();
// Get the answers, make a list
@ -1314,12 +1314,16 @@ HOTSPOT;
<div class="btn-group" data-toggle="buttons">
<label class="btn btn-default active"
aria-label="'.get_lang('AddAnnotationPath').'">
<input type="radio" value="0" name="'.$questionId.'-options" autocomplete="off" checked>
<input
type="radio" value="0"
name="'.$questionId.'-options" autocomplete="off" checked>
<span class="fa fa-pencil" aria-hidden="true"></span>
</label>
<label class="btn btn-default"
aria-label="'.get_lang('AddAnnotationText').'">
<input type="radio" value="1" name="'.$questionId.'-options" autocomplete="off">
<input
type="radio" value="1"
name="'.$questionId.'-options" autocomplete="off">
<span class="fa fa-font fa-fw" aria-hidden="true"></span>
</label>
</div>
@ -1365,6 +1369,11 @@ HOTSPOT;
$res_fb_type = Database::query($sql);
$result = Database::fetch_array($res_fb_type, 'ASSOC');
$result['duration_formatted'] = '';
if (!empty($result['exe_duration'])) {
$time = api_format_time($result['exe_duration'], 'js');
$result['duration_formatted'] = $time;
}
}
return $result;
@ -1673,6 +1682,7 @@ HOTSPOT;
* @param bool $showSessionField
* @param bool $showExerciseCategories
* @param array $userExtraFieldsToAdd
* @param bool $useCommaAsDecimalPoint
*
* @return array
*/
@ -1687,7 +1697,8 @@ HOTSPOT;
$courseCode = null,
$showSessionField = false,
$showExerciseCategories = false,
$userExtraFieldsToAdd = []
$userExtraFieldsToAdd = [],
$useCommaAsDecimalPoint = false
) {
//@todo replace all this globals
global $documentPath, $filter;
@ -1700,7 +1711,12 @@ HOTSPOT;
}
$course_id = $courseInfo['real_id'];
$is_allowedToEdit = api_is_allowed_to_edit(null, true) || api_is_allowed_to_edit(true) || api_is_drh() || api_is_student_boss();
$is_allowedToEdit =
api_is_allowed_to_edit(null, true) ||
api_is_allowed_to_edit(true) ||
api_is_drh() ||
api_is_student_boss() ||
api_is_session_admin();
$TBL_USER = Database::get_main_table(TABLE_MAIN_USER);
$TBL_EXERCICES = Database::get_course_table(TABLE_QUIZ_TEST);
$TBL_GROUP_REL_USER = Database::get_course_table(TABLE_GROUP_USER);
@ -1927,9 +1943,7 @@ HOTSPOT;
return $rowx[0];
}
$teacher_list = CourseManager::get_teacher_list_from_course_code(
$courseCode
);
$teacher_list = CourseManager::get_teacher_list_from_course_code($courseCode);
$teacher_id_list = [];
if (!empty($teacher_list)) {
foreach ($teacher_list as $teacher) {
@ -1937,6 +1951,15 @@ HOTSPOT;
}
}
$scoreDisplay = new ScoreDisplay();
$decimalSeparator = '.';
$thousandSeparator = ',';
if ($useCommaAsDecimalPoint) {
$decimalSeparator = ',';
$thousandSeparator = '';
}
$listInfo = [];
// Simple exercises
if (empty($hotpotatoe_where)) {
@ -1954,7 +1977,6 @@ HOTSPOT;
while ($rowx = Database::fetch_array($resx, 'ASSOC')) {
$results[] = $rowx;
}
$group_list = GroupManager::get_group_list(null, $courseInfo);
$clean_group_list = [];
if (!empty($group_list)) {
@ -1967,7 +1989,7 @@ HOTSPOT;
$lp_list = $lp_list_obj->get_flat_list();
$oldIds = array_column($lp_list, 'lp_old_id', 'iid');
if (is_array($results)) {
if (!empty($results)) {
$users_array_id = [];
$from_gradebook = false;
if (isset($_GET['gradebook']) && $_GET['gradebook'] == 'view') {
@ -1975,11 +1997,7 @@ HOTSPOT;
}
$sizeof = count($results);
$user_list_id = [];
$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++) {
@ -2061,10 +2079,9 @@ HOTSPOT;
// we filter the results if we have the permission to
$result_disabled = 0;
if (isset($results[$i]['results_disabled'])) {
$result_disabled = intval(
$results[$i]['results_disabled']
);
$result_disabled = (int) $results[$i]['results_disabled'];
}
if ($result_disabled == 0) {
$my_res = $results[$i]['exe_result'];
$my_total = $results[$i]['exe_weighting'];
@ -2076,9 +2093,19 @@ HOTSPOT;
$my_res = 0;
}
$score = self::show_score($my_res, $my_total);
$score = self::show_score(
$my_res,
$my_total,
true,
true,
false,
false,
$decimalSeparator,
$thousandSeparator
);
$actions = '<div class="pull-right">';
if ($is_allowedToEdit) {
if (isset($teacher_id_list)) {
if (in_array(
@ -2163,7 +2190,7 @@ HOTSPOT;
}
// Admin can always delete the attempt
if (($locked == false || api_is_platform_admin()) && !api_is_student_boss()) {
if (($locked == false || (api_is_platform_admin()) && !api_is_student_boss())) {
$ip = Tracking::get_ip_from_user_event(
$results[$i]['exe_user_id'],
api_get_utc_datetime(),
@ -2208,6 +2235,9 @@ HOTSPOT;
if (api_is_drh() && !api_is_platform_admin()) {
$delete_link = null;
}
if (api_is_session_admin()) {
$delete_link = '';
}
if ($revised == 3) {
$delete_link = null;
}
@ -2295,7 +2325,9 @@ HOTSPOT;
$category_was_added_for_this_test = true;
}
if (isset($objQuestionTmp->category_list) && !empty($objQuestionTmp->category_list)) {
if (isset($objQuestionTmp->category_list) &&
!empty($objQuestionTmp->category_list)
) {
foreach ($objQuestionTmp->category_list as $category_id) {
$category_list[$category_id]['score'] += $my_total_score;
$category_list[$category_id]['total'] += $my_total_weight;
@ -2321,7 +2353,17 @@ HOTSPOT;
}
foreach ($category_list as $categoryId => $result) {
$scoreToDisplay = self::show_score($result['score'], $result['total']);
$scoreToDisplay = self::show_score(
$result['score'],
$result['total'],
true,
true,
false,
false,
$decimalSeparator,
$thousandSeparator
);
$results[$i]['category_'.$categoryId] = $scoreToDisplay;
$results[$i]['category_'.$categoryId.'_score_percentage'] = self::show_score(
$result['score'],
@ -2329,7 +2371,9 @@ HOTSPOT;
true,
true,
true, // $show_only_percentage = false
true // hide % sign
true, // hide % sign
$decimalSeparator,
$thousandSeparator
);
$results[$i]['category_'.$categoryId.'_only_score'] = $result['score'];
$results[$i]['category_'.$categoryId.'_total'] = $result['total'];
@ -2344,10 +2388,23 @@ HOTSPOT;
true,
true,
true,
true
true,
$decimalSeparator,
$thousandSeparator
);
$results[$i]['only_score'] = $scoreDisplay->format_score(
$my_res,
false,
$decimalSeparator,
$thousandSeparator
);
$results[$i]['total'] = $scoreDisplay->format_score(
$my_total,
false,
$decimalSeparator,
$thousandSeparator
);
$results[$i]['only_score'] = $my_res;
$results[$i]['total'] = $my_total;
$results[$i]['lp'] = $lp_name;
$results[$i]['actions'] = $actions;
$listInfo[] = $results[$i];
@ -2421,12 +2478,14 @@ HOTSPOT;
* Converts the score with the exercise_max_note and exercise_min_score
* the platform settings + formats the results using the float_format function.
*
* @param float $score
* @param float $weight
* @param bool $show_percentage show percentage or not
* @param bool $use_platform_settings use or not the platform settings
* @param bool $show_only_percentage
* @param bool $hidePercetangeSign hide "%" sign
* @param float $score
* @param float $weight
* @param bool $show_percentage show percentage or not
* @param bool $use_platform_settings use or not the platform settings
* @param bool $show_only_percentage
* @param bool $hidePercentageSign hide "%" sign
* @param string $decimalSeparator
* @param string $thousandSeparator
*
* @return string an html with the score modified
*/
@ -2436,7 +2495,9 @@ HOTSPOT;
$show_percentage = true,
$use_platform_settings = true,
$show_only_percentage = false,
$hidePercetangeSign = false
$hidePercentageSign = false,
$decimalSeparator = '.',
$thousandSeparator = ','
) {
if (is_null($score) && is_null($weight)) {
return '-';
@ -2458,14 +2519,13 @@ HOTSPOT;
$percentage = (100 * $score) / ($weight != 0 ? $weight : 1);
// Formats values
$percentage = float_format($percentage, 1);
$score = float_format($score, 1);
$weight = float_format($weight, 1);
$percentage = float_format($percentage, 1, $decimalSeparator, $thousandSeparator);
$score = float_format($score, 1, $decimalSeparator, $thousandSeparator);
$weight = float_format($weight, 1, $decimalSeparator, $thousandSeparator);
$html = '';
if ($show_percentage) {
$percentageSign = '%';
if ($hidePercetangeSign) {
if ($hidePercentageSign) {
$percentageSign = '';
}
$html = $percentage."$percentageSign ($score / $weight)";
@ -2495,9 +2555,7 @@ HOTSPOT;
*/
public static function getModelStyle($model, $percentage)
{
//$modelWithStyle = get_lang($model['name']);
$modelWithStyle = '<span class="'.$model['css_class'].'"> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>';
//$modelWithStyle .= $percentage;
return $modelWithStyle;
}
@ -3257,10 +3315,7 @@ EOT;
$avg_score = 0;
if (!empty($user_results)) {
foreach ($user_results as $result) {
if (!empty($result['exe_weighting']) && intval(
$result['exe_weighting']
) != 0
) {
if (!empty($result['exe_weighting']) && intval($result['exe_weighting']) != 0) {
$score = $result['exe_result'] / $result['exe_weighting'];
$avg_score += $score;
}
@ -3456,7 +3511,7 @@ EOT;
* @param int $question_id
* @param int $exercise_id
*
* @return int
* @return array
*/
public static function getNumberStudentsFillBlanksAnswerCount(
$question_id,
@ -3480,7 +3535,6 @@ EOT;
);
$arrayCount = [];
foreach ($listFillTheBlankResult as $resultCount) {
foreach ($resultCount as $index => $count) {
//this is only for declare the array index per answer
@ -3697,7 +3751,6 @@ EOT;
$answer_id = intval($answer_id);
$exercise_id = intval($exercise_id);
$courseId = api_get_course_int_id($course_code);
$course_code = Database::escape_string($course_code);
$session_id = intval($session_id);
switch ($question_type) {
@ -3944,43 +3997,6 @@ EOT;
return $return;
}
/**
* @param string $in_name is the name and the id of the <select>
* @param string $in_default default value for option
* @param string $in_onchange
*
* @return string the html code of the <select>
*/
public static function displayGroupMenu($in_name, $in_default, $in_onchange = "")
{
// check the default value of option
$tabSelected = [$in_default => " selected='selected' "];
$res = "";
$res .= "<select name='$in_name' id='$in_name' onchange='".$in_onchange."' >";
$res .= "<option value='-1'".$tabSelected["-1"].">-- ".get_lang(
'AllGroups'
)." --</option>";
$res .= "<option value='0'".$tabSelected["0"].">- ".get_lang(
'NotInAGroup'
)." -</option>";
$tabGroups = GroupManager::get_group_list();
$currentCatId = 0;
for ($i = 0; $i < count($tabGroups); $i++) {
$tabCategory = GroupManager::get_category_from_group(
$tabGroups[$i]['iid']
);
if ($tabCategory["id"] != $currentCatId) {
$res .= "<option value='-1' disabled='disabled'>".$tabCategory["title"]."</option>";
$currentCatId = $tabCategory["id"];
}
$res .= "<option ".$tabSelected[$tabGroups[$i]["id"]]."style='margin-left:40px' value='".$tabGroups[$i]["id"]."'>".
$tabGroups[$i]["name"]."</option>";
}
$res .= "</select>";
return $res;
}
/**
* @param int $exe_id
*/
@ -4107,14 +4123,9 @@ EOT;
$user_info = api_get_user_info($exercise_stat_info['exe_user_id']);
if ($user_info) {
// Shows exercise header
echo $objExercise->show_exercise_result_header(
echo $objExercise->showExerciseResultHeader(
$user_info,
api_convert_and_format_date(
$exercise_stat_info['start_date'],
DATE_TIME_FORMAT_LONG
),
$exercise_stat_info['duration'],
$exercise_stat_info['user_ip']
$exercise_stat_info
);
}
}
@ -4186,6 +4197,7 @@ EOT;
'question' => $result['open_question'],
'answer' => $result['open_answer'],
'answer_type' => $result['answer_type'],
'generated_oral_file' => $result['generated_oral_file'],
];
$my_total_score = $result['score'];
@ -4295,11 +4307,18 @@ EOT;
if ($show_results) {
$question_content .= '</div>';
}
if (!$show_only_score) {
if ($objExercise->showExpectedChoice()) {
$exercise_content .= Display::div(
Display::panel($question_content),
['class' => 'question-panel']
);
} else {
if (!$show_only_score) {
$exercise_content .= Display::div(
Display::panel($question_content),
['class' => 'question-panel']
);
}
}
} // end foreach() block that loops over all questions
}
@ -4330,7 +4349,8 @@ EOT;
}
if ($show_all_but_expected_answer) {
$exercise_content .= "<div class='normal-message'>".get_lang('ExerciseWithFeedbackWithoutCorrectionComment')."</div>";
$exercise_content .= "<div class='normal-message'>".
get_lang('ExerciseWithFeedbackWithoutCorrectionComment')."</div>";
}
// Remove audio auto play from questions on results page - refs BT#7939
@ -4341,6 +4361,12 @@ EOT;
);
echo $total_score_text;
// Ofaj change BT#11784
if (!empty($objExercise->description)) {
echo Display::div($objExercise->description, ['class' => 'exercise_description']);
}
echo $exercise_content;
if (!$show_only_score) {
@ -4392,24 +4418,6 @@ EOT;
}
}
/**
* @param string $class
* @param string $scoreLabel
* @param string $result
*
* @return string
*/
public static function getQuestionRibbon($class, $scoreLabel, $result)
{
return '<div class="ribbon">
<div class="rib rib-'.$class.'">
<h3>'.$scoreLabel.'</h3>
</div>
<h4>'.get_lang('Score').': '.$result.'</h4>
</div>'
;
}
/**
* @param Exercise $objExercise
* @param float $score

@ -45,71 +45,84 @@ class ExerciseShowFunctions
$resultsDisabled,
$showTotalScoreAndUserChoices
);
if (strpos($originalStudentAnswer, 'font color') !== false) {
// ofaj
/*if (strpos($originalStudentAnswer, 'font color') !== false) {
$answerHTML = $originalStudentAnswer;
}
}*/
if (empty($id)) {
echo '<tr><td>';
echo Security::remove_XSS($answerHTML, COURSEMANAGERLOWSECURITY);
echo '</td></tr>';
} else {
?>
<tr>
<td>
<?php echo Security::remove_XSS($answerHTML, COURSEMANAGERLOWSECURITY); ?>
</td>
<?php
if (!api_is_allowed_to_edit(null, true) && $feedbackType != EXERCISE_FEEDBACK_TYPE_EXAM) {
?>
<td>
<?php
$comm = Event::get_comments($id, $questionId); ?>
</td>
<?php
} ?>
</tr>
<?php
echo '<tr><td>';
echo Security::remove_XSS($answerHTML, COURSEMANAGERLOWSECURITY);
echo '</td>';
if (!api_is_allowed_to_edit(null, true) && $feedbackType != EXERCISE_FEEDBACK_TYPE_EXAM) {
echo '<td>';
$comm = Event::get_comments($id, $questionId);
echo '</td>';
}
echo '</tr>';
}
}
/**
* Shows the answer to a calculated question, as HTML.
*
* @param Exercise $exercise
* @param string Answer text
* @param int Exercise ID
* @param int Question ID
*/
public static function display_calculated_answer(
$exercise,
$feedback_type,
$answer,
$id,
$questionId,
$results_disabled,
$showTotalScoreAndUserChoices
$showTotalScoreAndUserChoices,
$expectedChoice = '',
$choice = '',
$status = ''
) {
if (empty($id)) {
echo '<tr><td>'.Security::remove_XSS($answer).'</td></tr>';
if ($exercise->showExpectedChoice()) {
if (empty($id)) {
echo '<tr><td>'.Security::remove_XSS($answer).'</td>';
echo '<td>'.Security::remove_XSS($choice).'</td>';
echo '<td>'.Security::remove_XSS($expectedChoice).'</td>';
echo '<td>'.Security::remove_XSS($status).'</td>';
echo '</tr>';
} else {
echo '<tr><td>';
echo Security::remove_XSS($answer);
echo '</td><td>';
echo Security::remove_XSS($choice);
echo '</td><td>';
echo Security::remove_XSS($expectedChoice);
echo '</td><td>';
echo Security::remove_XSS($status);
echo '</td>';
if (!api_is_allowed_to_edit(null, true) && $feedback_type != EXERCISE_FEEDBACK_TYPE_EXAM) {
echo '<td>';
$comm = Event::get_comments($id, $questionId);
echo '</td>';
}
echo '</tr>';
}
} else {
?>
<tr>
<td>
<?php
echo Security::remove_XSS($answer); ?>
</td>
<?php
if (!api_is_allowed_to_edit(null, true) && $feedback_type != EXERCISE_FEEDBACK_TYPE_EXAM) {
?>
<td>
<?php
$comm = Event::get_comments($id, $questionId); ?>
</td>
<?php
} ?>
</tr>
<?php
if (empty($id)) {
echo '<tr><td>'.Security::remove_XSS($answer).'</td></tr>';
} else {
echo '<tr><td>';
echo Security::remove_XSS($answer);
if (!api_is_allowed_to_edit(null, true) && $feedback_type != EXERCISE_FEEDBACK_TYPE_EXAM) {
echo '<td>';
$comm = Event::get_comments($id, $questionId);
echo '</td>';
}
echo '</tr>';
}
}
}
@ -228,10 +241,9 @@ class ExerciseShowFunctions
}
if ($resultsDisabled == RESULT_DISABLE_SHOW_SCORE_ATTEMPT_SHOW_ANSWERS_LAST_ATTEMPT) {
$hide_expected_answer = true;
if ($showTotalScoreAndUserChoices) {
$hide_expected_answer = false;
} else {
$hide_expected_answer = true;
}
}
@ -250,57 +262,54 @@ class ExerciseShowFunctions
"#ED2024",
"#3B3B3B",
"#F7BDE2",
]; ?>
<table class="data_table">
<tr>
<td class="text-center" width="5%">
<span class="fa fa-square fa-fw fa-2x" aria-hidden="true" style="color: <?php echo $hotspot_colors[$orderColor]; ?>"></span>
</td>
<td class="text-left" width="25%">
<?php echo "$answerId - $answer"; ?>
</td>
<td class="text-left" width="10%">
<?php
if (!$hide_expected_answer) {
$my_choice = $studentChoice ? get_lang('Correct') : get_lang('Fault');
echo $my_choice;
} ?>
</td>
<?php if ($feedback_type != EXERCISE_FEEDBACK_TYPE_EXAM) {
?>
<td class="text-left" width="60%">
<?php
if ($studentChoice) {
echo '<span style="font-weight: bold; color: #008000;">'.nl2br($answerComment).'</span>';
} ?>
</td>
<?php
} else {
?>
<td class="text-left" width="60%">&nbsp;</td>
<?php
} ?>
</tr>
<?php
];
echo '<table class="data_table"><tr>';
echo '<td class="text-center" width="5%">';
echo '<span class="fa fa-square fa-fw fa-2x" aria-hidden="true" style="color:'.$hotspot_colors[$orderColor].'"></span>';
echo '</td>';
echo '<td class="text-left" width="25%">';
echo "$answerId - $answer";
echo '</td>';
echo '<td class="text-left" width="10%">';
if (!$hide_expected_answer) {
$status = Display::label(get_lang('Incorrect'), 'danger');
if ($studentChoice) {
$status = Display::label(get_lang('Correct'), 'success');
}
echo $status;
}
echo '</td>';
if ($feedback_type != EXERCISE_FEEDBACK_TYPE_EXAM) {
echo '<td class="text-left" width="60%">';
if ($studentChoice) {
echo '<span style="font-weight: bold; color: #008000;">'.nl2br($answerComment).'</span>';
}
echo '</td>';
} else {
echo '<td class="text-left" width="60%">&nbsp;</td>';
}
echo '</tr>';
}
/**
* Display the answers to a multiple choice question.
*
* @param int $feedback_type Feedback type
* @param int $answerType Answer type
* @param int $studentChoice Student choice
* @param string $answer Textual answer
* @param string $answerComment Comment on answer
* @param string $answerCorrect Correct answer comment
* @param int $id Exercise ID
* @param int $questionId Question ID
* @param bool $ans Whether to show the answer comment or not
* @param bool $resultsDisabled
* @param bool $showTotalScoreAndUserChoices
* @param bool $export
* @param Exercise $exercise
* @param int $feedback_type Feedback type
* @param int $answerType Answer type
* @param int $studentChoice Student choice
* @param string $answer Textual answer
* @param string $answerComment Comment on answer
* @param string $answerCorrect Correct answer comment
* @param int $id Exercise ID
* @param int $questionId Question ID
* @param bool $ans Whether to show the answer comment or not
* @param bool $resultsDisabled
* @param bool $showTotalScoreAndUserChoices
* @param bool $export
*/
public static function display_unique_or_multiple_answer(
$exercise,
$feedback_type,
$answerType,
$studentChoice,
@ -332,10 +341,9 @@ class ExerciseShowFunctions
}
if ($resultsDisabled == RESULT_DISABLE_SHOW_SCORE_ATTEMPT_SHOW_ANSWERS_LAST_ATTEMPT) {
$hide_expected_answer = true;
if ($showTotalScoreAndUserChoices) {
$hide_expected_answer = false;
} else {
$hide_expected_answer = true;
}
}
@ -344,56 +352,59 @@ class ExerciseShowFunctions
$icon .= '.png';
$iconAnswer = in_array($answerType, [UNIQUE_ANSWER, UNIQUE_ANSWER_NO_OPTION]) ? 'radio' : 'checkbox';
$iconAnswer .= $answerCorrect ? '_on' : '_off';
$iconAnswer .= '.png'; ?>
<tr>
<td width="5%">
<?php echo Display::return_icon($icon, null, null, ICON_SIZE_TINY); ?>
</td>
<td width="5%">
<?php if (!$hide_expected_answer) {
$iconAnswer .= '.png';
echo '<tr>';
echo '<td width="5%">';
echo Display::return_icon($icon, null, null, ICON_SIZE_TINY);
echo '</td><td width="5%">';
if (!$hide_expected_answer) {
echo Display::return_icon($iconAnswer, null, null, ICON_SIZE_TINY);
} else {
echo "-";
} ?>
</td>
<td width="40%">
<?php
echo $answer; ?>
</td>
}
echo '</td><td width="40%">';
echo $answer;
echo '</td>';
if ($exercise->showExpectedChoice()) {
$status = Display::label(get_lang('Incorrect'), 'danger');
if ($studentChoice) {
if ($answerCorrect) {
$status = Display::label(get_lang('Correct'), 'success');
}
}
echo '<td width="20%">';
echo $status;
echo '</td>';
}
<?php if ($feedback_type != EXERCISE_FEEDBACK_TYPE_EXAM) {
?>
<td width="20%">
<?php
if ($feedback_type != EXERCISE_FEEDBACK_TYPE_EXAM) {
echo '<td width="20%">';
if ($studentChoice) {
$color = 'black';
if ($answerCorrect) {
$color = 'green';
} else {
$color = 'black';
}
if ($hide_expected_answer) {
$color = '';
}
echo '<span style="font-weight: bold; color: '.$color.';">'.nl2br($answerComment).'</span>';
} ?>
</td>
<?php
echo '<span style="font-weight: bold; color: '.$color.';">'.strip_tags($answerComment).'</span>';
}
echo '</td>';
if ($ans == 1) {
$comm = Event::get_comments($id, $questionId);
} ?>
<?php
} else {
?>
<td>&nbsp;</td>
<?php
} ?>
</tr>
<?php
}
} else {
echo '<td>&nbsp;</td>';
}
echo '</tr>';
}
/**
* Display the answers to a multiple choice question.
*
* @param Exercise $exercise
* @param int Answer type
* @param int Student choice
* @param string Textual answer
@ -404,6 +415,7 @@ class ExerciseShowFunctions
* @param bool Whether to show the answer comment or not
*/
public static function display_multiple_answer_true_false(
$exercise,
$feedback_type,
$answerType,
$studentChoice,
@ -417,35 +429,27 @@ class ExerciseShowFunctions
$showTotalScoreAndUserChoices
) {
$hide_expected_answer = false;
if ($feedback_type == 0 && ($resultsDisabled == RESULT_DISABLE_SHOW_SCORE_ONLY)) {
$hide_expected_answer = true;
}
if ($resultsDisabled == RESULT_DISABLE_SHOW_SCORE_ATTEMPT_SHOW_ANSWERS_LAST_ATTEMPT) {
$hide_expected_answer = true;
if ($showTotalScoreAndUserChoices) {
$hide_expected_answer = false;
} else {
$hide_expected_answer = true;
}
} ?>
<tr>
<td width="5%">
<?php
}
echo '<tr><td width="5%">';
$course_id = api_get_course_int_id();
$new_options = Question::readQuestionOption($questionId, $course_id);
//Your choice
// Your choice
if (isset($new_options[$studentChoice])) {
echo get_lang($new_options[$studentChoice]['name']);
} else {
echo '-';
} ?>
</td>
<td width="5%">
<?php
//Expected choice
}
echo '</td><td width="5%">';
// Expected choice
if (!$hide_expected_answer) {
if (isset($new_options[$answerCorrect])) {
echo get_lang($new_options[$answerCorrect]['name']);
@ -454,16 +458,23 @@ class ExerciseShowFunctions
}
} else {
echo '-';
} ?>
</td>
<td width="40%">
<?php echo $answer; ?>
</td>
<?php if ($feedback_type != EXERCISE_FEEDBACK_TYPE_EXAM) {
?>
<td width="20%">
<?php
}
echo '</td><td width="40%">';
echo $answer;
echo '</td>';
if ($exercise->showExpectedChoice()) {
$status = Display::label(get_lang('Incorrect'), 'danger');
if (isset($new_options[$studentChoice])) {
if ($studentChoice == $answerCorrect) {
$status = Display::label(get_lang('Correct'), 'success');
}
}
echo '<td width="20%">';
echo $status;
echo '</td>';
}
if ($feedback_type != EXERCISE_FEEDBACK_TYPE_EXAM) {
echo '<td width="20%">';
$color = "black";
if (isset($new_options[$studentChoice])) {
if ($studentChoice == $answerCorrect) {
@ -473,27 +484,22 @@ class ExerciseShowFunctions
if ($hide_expected_answer) {
$color = '';
}
echo '<span style="font-weight: bold; color: '.$color.';">'.nl2br($answerComment).'</span>';
} ?>
</td>
<?php
}
echo '</td>';
if ($ans == 1) {
$comm = Event::get_comments($id, $questionId);
} ?>
<?php
}
} else {
?>
<td>&nbsp;</td>
<?php
} ?>
</tr>
<?php
echo '<td>&nbsp;</td>';
}
echo '</tr>';
}
/**
* Display the answers to a multiple choice question.
*
* @param Exercise $exercise
* @param int Answer type
* @param int Student choice
* @param string Textual answer
@ -504,6 +510,7 @@ class ExerciseShowFunctions
* @param bool Whether to show the answer comment or not
*/
public static function display_multiple_answer_combination_true_false(
$exercise,
$feedback_type,
$answerType,
$studentChoice,
@ -522,26 +529,22 @@ class ExerciseShowFunctions
}
if ($resultsDisabled == RESULT_DISABLE_SHOW_SCORE_ATTEMPT_SHOW_ANSWERS_LAST_ATTEMPT) {
$hide_expected_answer = true;
if ($showTotalScoreAndUserChoices) {
$hide_expected_answer = false;
} else {
$hide_expected_answer = true;
}
} ?>
<tr>
<td width="5%">
<?php
//Your choice
}
echo '<tr><td width="5%">';
// Your choice
$question = new MultipleAnswerCombinationTrueFalse();
if (isset($question->options[$studentChoice])) {
echo $question->options[$studentChoice];
} else {
echo $question->options[2];
} ?>
</td>
<td width="5%">
<?php
//Expected choice
}
echo '</td><td width="5%">';
// Expected choice
if (!$hide_expected_answer) {
if (isset($question->options[$answerCorrect])) {
echo $question->options[$answerCorrect];
@ -550,18 +553,28 @@ class ExerciseShowFunctions
}
} else {
echo '-';
} ?>
</td>
<td width="40%">
<?php
//my answer
echo $answer; ?>
</td>
<?php
}
echo '</td>';
echo '<td width="40%">';
// my answer
echo $answer;
echo '</td>';
if ($exercise->showExpectedChoice()) {
$status = '';
if (isset($studentChoice)) {
$status = Display::label(get_lang('Incorrect'), 'danger');
if ($studentChoice == $answerCorrect) {
$status = Display::label(get_lang('Correct'), 'success');
}
}
echo '<td width="20%">';
echo $status;
echo '</td>';
}
if ($feedback_type != EXERCISE_FEEDBACK_TYPE_EXAM) {
?>
<td width="20%">
<?php
echo '<td width="20%">';
//@todo replace this harcoded value
if ($studentChoice) {
$color = "black";
@ -572,9 +585,8 @@ class ExerciseShowFunctions
$color = '';
}
echo '<span style="font-weight: bold; color: '.$color.';">'.nl2br($answerComment).'</span>';
} ?>
</td>
<?php
}
echo '</td>';
if ($ans == 1) {
$comm = Event::get_comments($id, $questionId);
}

@ -43,10 +43,9 @@ class Export
}
$filePath = api_get_path(SYS_ARCHIVE_PATH).uniqid('').'.csv';
$writer = new CsvWriter();
$writer->setStream(fopen($filePath, 'w'));
$stream = fopen($filePath, 'w');
$writer = new CsvWriter(';', '"', $stream, true);
$writer->prepare();
foreach ($data as $item) {
if (empty($item)) {
$writer->writeItem([]);
@ -78,7 +77,6 @@ class Export
$file = new \SplFileObject($filePath, 'w');
$writer = new ExcelWriter($file);
$writer->prepare();
foreach ($data as $row) {
$writer->writeItem($row);
}

@ -140,6 +140,9 @@ class ExtraField extends Model
case 'survey':
$this->extraFieldType = EntityExtraField::SURVEY_FIELD_TYPE;
break;
case 'scheduled_announcement':
$this->extraFieldType = EntityExtraField::SCHEDULED_ANNOUNCEMENT;
break;
}
$this->pageUrl = 'extra_fields.php?type='.$this->type;
@ -160,7 +163,7 @@ class ExtraField extends Model
*/
public static function getValidExtraFieldTypes()
{
return [
$result = [
'user',
'course',
'session',
@ -174,6 +177,12 @@ class ExtraField extends Model
'user_certificate',
'survey',
];
if (api_get_configuration_value('allow_scheduled_announcements')) {
$result[] = 'scheduled_announcement';
}
return $result;
}
/**
@ -893,19 +902,22 @@ class ExtraField extends Model
public function save($params, $show_query = false)
{
$fieldInfo = self::get_handler_field_info_by_field_variable($params['variable']);
$params = self::clean_parameters($params);
$params = $this->clean_parameters($params);
$params['extra_field_type'] = $this->extraFieldType;
if ($fieldInfo) {
return $fieldInfo['id'];
} else {
$id = parent::save($params, $show_query);
if ($id) {
$session_field_option = new ExtraFieldOption($this->type);
$params['field_id'] = $id;
$session_field_option->save($params);
if (!$id) {
return 0;
}
$session_field_option = new ExtraFieldOption($this->type);
$params['field_id'] = $id;
$session_field_option->save($params);
return $id;
}
}
@ -915,7 +927,7 @@ class ExtraField extends Model
*/
public function update($params, $showQuery = false)
{
$params = self::clean_parameters($params);
$params = $this->clean_parameters($params);
if (isset($params['id'])) {
$field_option = new ExtraFieldOption($this->type);
$params['field_id'] = $params['id'];
@ -925,7 +937,7 @@ class ExtraField extends Model
$field_option->save($params, $showQuery);
}
parent::update($params, $showQuery);
return parent::update($params, $showQuery);
}
/**
@ -1150,8 +1162,8 @@ class ExtraField extends Model
break;
case self::FIELD_TYPE_SELECT_MULTIPLE:
$options = [];
foreach ($field_details['options'] as $option_id => $option_details) {
$options[$option_details['option_value']] = $option_details['display_text'];
foreach ($field_details['options'] as $optionDetails) {
$options[$optionDetails['option_value']] = $optionDetails['display_text'];
}
$form->addElement(
'select',
@ -1446,12 +1458,12 @@ class ExtraField extends Model
$form->applyFilter('extra_'.$field_details['variable'], 'stripslashes');
$form->applyFilter('extra_'.$field_details['variable'], 'trim');
$allowed_picture_types = ['jpg', 'jpeg', 'png', 'gif'];
$allowedPictureTypes = ['jpg', 'jpeg', 'png', 'gif'];
$form->addRule(
'extra_'.$field_details['variable'],
get_lang('OnlyImagesAllowed').' ('.implode(',', $allowed_picture_types).')',
get_lang('OnlyImagesAllowed').' ('.implode(',', $allowedPictureTypes).')',
'filetype',
$allowed_picture_types
$allowedPictureTypes
);
if ($freezeElement) {
@ -1484,14 +1496,45 @@ class ExtraField extends Model
array_key_exists($fieldVariable, $extraData)
) {
if (file_exists(api_get_path(SYS_UPLOAD_PATH).$extraData[$fieldVariable])) {
$fieldTexts[] = Display::url(
api_get_path(WEB_UPLOAD_PATH).$extraData[$fieldVariable],
$linkToDelete = '';
$divItemId = $field_details['variable'];
if (api_is_platform_admin()) {
$url = api_get_path(WEB_AJAX_PATH).'extra_field.ajax.php?type='.$this->type;
$url .= '&a=delete_file&field_id='.$field_details['id'].'&item_id='.$itemId;
$deleteId = $field_details['variable'].'_delete';
$form->addHtml("
<script>
$(document).ready(function() {
$('#".$deleteId."').on('click', function() {
$.ajax({
type: 'GET',
url: '".$url."',
success: function(result) {
if (result == 1) {
$('#".$divItemId."').html('".get_lang('Deleted')."');
}
}
});
});
});
</script>
");
$linkToDelete = '&nbsp;'.Display::url(
Display::return_icon('delete.png', get_lang('Delete')),
'javascript:void(0)',
['id' => $deleteId]
);
}
$fieldTexts[] = '<div id="'.$divItemId.'">'.Display::url(
basename($extraData[$fieldVariable]),
api_get_path(WEB_UPLOAD_PATH).$extraData[$fieldVariable],
[
'title' => $field_details['display_text'],
'target' => '_blank',
]
);
).$linkToDelete.'</div>';
}
}
@ -1614,7 +1657,7 @@ class ExtraField extends Model
$('#map_extra_{$field_details['variable']}')
.html('<div class=\"alert alert-info\">"
.get_lang('YouNeedToActivateTheGoogleMapsPluginInAdminPlatformToSeeTheMap')
.addslashes(get_lang('YouNeedToActivateTheGoogleMapsPluginInAdminPlatformToSeeTheMap'))
."</div>');
});
@ -1635,7 +1678,6 @@ class ExtraField extends Model
var geoOptions = {
enableHighAccuracy: true
};
navigator.geolocation.getCurrentPosition(geoPosition, geoError, geoOptions);
}
}

@ -104,12 +104,14 @@ class ExtraFieldValue extends Model
}
$type = $this->getExtraField()->getExtraFieldType();
$extraField = new ExtraField($this->type);
$extraFields = $extraField->get_all(null, 'option_order');
// Parse params.
foreach ($extraFields as $fieldDetails) {
$field_variable = $fieldDetails['variable'];
// if the field is not visible to the user in the end, we need to apply special rules
if ($fieldDetails['visible_to_self'] != 1) {
//only admins should be able to add those values
@ -125,10 +127,9 @@ class ExtraFieldValue extends Model
continue;
}
$value = '';
if (isset($params['extra_'.$field_variable])) {
$value = $params['extra_'.$field_variable];
} else {
$value = '';
}
$extraFieldInfo = $this->getExtraField()->get_handler_field_info_by_field_variable($field_variable);
@ -214,7 +215,6 @@ class ExtraFieldValue extends Model
$fieldRelTag->setFieldId($extraFieldInfo['id']);
$fieldRelTag->setItemId($params['item_id']);
$fieldRelTag->setTagId($tag->getId());
$em->persist($fieldRelTag);
}
@ -260,7 +260,7 @@ class ExtraFieldValue extends Model
'value' => $fileDirStored.$fileName,
'comment' => $comment,
];
self::save($newParams);
$this->save($newParams);
}
break;
case ExtraField::FIELD_TYPE_FILE:
@ -281,6 +281,10 @@ class ExtraFieldValue extends Model
$fileDir = api_get_path(SYS_UPLOAD_PATH).'work/';
$fileDirStored = "work/";
break;
case 'scheduled_announcement':
$fileDir = api_get_path(SYS_UPLOAD_PATH).'scheduled_announcement/';
$fileDirStored = 'scheduled_announcement/';
break;
}
$cleanedName = api_replace_dangerous_char($value['name']);
@ -302,7 +306,7 @@ class ExtraFieldValue extends Model
$new_params['comment'] = $comment;
}
self::save($new_params);
$this->save($new_params);
}
break;
case ExtraField::FIELD_TYPE_CHECKBOX:
@ -320,7 +324,7 @@ class ExtraFieldValue extends Model
'comment' => $comment,
];
self::save($newParams);
$this->save($newParams);
break;
default:
@ -330,7 +334,7 @@ class ExtraFieldValue extends Model
'value' => $value,
'comment' => $comment,
];
self::save($newParams, $showQuery);
$this->save($newParams, $showQuery);
}
}
}
@ -401,12 +405,11 @@ class ExtraFieldValue extends Model
case ExtraField::FIELD_TYPE_DOUBLE_SELECT:
case ExtraField::FIELD_TYPE_SELECT_WITH_TEXT_FIELD:
if (is_array($value)) {
$value_to_insert = null;
if (isset($value['extra_'.$extraFieldInfo['variable']]) &&
isset($value['extra_'.$extraFieldInfo['variable'].'_second'])
) {
$value_to_insert = $value['extra_'.$extraFieldInfo['variable']].'::'.$value['extra_'.$extraFieldInfo['variable'].'_second'];
} else {
$value_to_insert = null;
}
}
break;
@ -994,20 +997,35 @@ class ExtraFieldValue extends Model
* @param int $itemId
* @param int $fieldId
* @param int $fieldValue
*
* @return bool
*/
public function deleteValuesByHandlerAndFieldAndValue($itemId, $fieldId, $fieldValue)
{
$itemId = intval($itemId);
$fieldId = intval($fieldId);
$fieldValue = Database::escape_string($fieldValue);
$sql = "DELETE FROM {$this->table}
$fieldData = $this->getExtraField()->get($fieldId);
if ($fieldData) {
$fieldValue = Database::escape_string($fieldValue);
$sql = "DELETE FROM {$this->table}
WHERE
item_id = '$itemId' AND
field_id = '$fieldId' AND
value = '$fieldValue'
";
Database::query($sql);
Database::query($sql);
// Delete file from uploads
if ($fieldData['field_type'] == ExtraField::FIELD_TYPE_FILE) {
api_remove_uploaded_file($this->type, basename($fieldValue));
}
return true;
}
return false;
}
/**

@ -276,7 +276,8 @@ function handle_uploaded_document(
$sessionId,
$groupId,
$output,
$onlyUploadFile
$onlyUploadFile,
$whatIfFileExists
);
} elseif ($unzip == 1 && !preg_match('/.zip$/', strtolower($uploadedFile['name']))) {
// We can only unzip ZIP files (no gz, tar,...)
@ -371,27 +372,6 @@ function handle_uploaded_document(
$sessionId
);
$documentList = DocumentManager::getDocumentByPathInCourse(
$courseInfo,
$filePath //$filePath
);
// This means that the path already exists in this course.
if (!empty($documentList) && $whatIfFileExists != 'overwrite') {
//$found = false;
// Checking if we are talking about the same course + session
/*foreach ($documentList as $document) {
if ($document['session_id'] == $sessionId) {
$found = true;
break;
}
}*/
//if ($found == false) {
//$whatIfFileExists = 'rename';
//}
}
// What to do if the target file exists
switch ($whatIfFileExists) {
// Overwrite the file if it exists
@ -481,11 +461,13 @@ function handle_uploaded_document(
// If the file is in a folder, we need to update all parent folders
item_property_update_on_folder($courseInfo, $uploadPath, $userId);
// Display success message with extra info to user
if ($output) {
Display::addFlash(
Display::return_message(
get_lang('UplUploadSucceeded').'<br /> '.$documentTitle.' '.get_lang('UplFileOverwritten'),
get_lang('UplUploadSucceeded').'<br /> '.
$documentTitle.' '.get_lang('UplFileOverwritten'),
'confirmation',
false
)
@ -622,7 +604,8 @@ function handle_uploaded_document(
if ($output) {
Display::addFlash(
Display::return_message(
get_lang('UplUploadSucceeded').'<br />'.get_lang('UplFileSavedAs').' '.$documentTitle,
get_lang('UplUploadSucceeded').'<br />'.
get_lang('UplFileSavedAs').' '.$documentTitle,
'success',
false
)
@ -644,12 +627,27 @@ function handle_uploaded_document(
return false;
}
break;
case 'nothing':
$fileExists = file_exists($fullPath);
if ($fileExists) {
if ($output) {
Display::addFlash(
Display::return_message(
$uploadPath.$cleanName.' '.get_lang('UplAlreadyExists'),
'warning',
false
)
);
}
break;
}
// no break
default:
// Only save the file if it doesn't exist or warn user if it does exist
if (file_exists($fullPath) && $docId) {
if ($output) {
Display::addFlash(
Display::return_message($cleanName.' '.get_lang('UplAlreadyExists'), 'error', false)
Display::return_message($cleanName.' '.get_lang('UplAlreadyExists'), 'warning', false)
);
}
} else {
@ -1104,16 +1102,17 @@ function unzip_uploaded_file($uploaded_file, $upload_path, $base_work_dir, $max_
*
* @param array $courseInfo
* @param array $userInfo
* @param array $uploaded_file - follows the $_FILES Structure
* @param string $uploadPath - destination of the upload.
* This path is to append to $base_work_dir
* @param string $base_work_dir - base working directory of the module
* @param int $maxFilledSpace - amount of bytes to not exceed in the base
* working directory
* @param array $uploaded_file - follows the $_FILES Structure
* @param string $uploadPath - destination of the upload.
* This path is to append to $base_work_dir
* @param string $base_work_dir - base working directory of the module
* @param int $maxFilledSpace - amount of bytes to not exceed in the base
* working directory
* @param int $sessionId
* @param int $groupId group.id
* @param bool $output Optional. If no output not wanted on success, set to false.
* @param int $groupId group.id
* @param bool $output Optional. If no output not wanted on success, set to false.
* @param bool $onlyUploadFile
* @param string $whatIfFileExists (only works if $onlyUploadFile is false)
*
* @return bool true if it succeeds false otherwise
*/
@ -1127,7 +1126,8 @@ function unzip_uploaded_document(
$sessionId = 0,
$groupId = 0,
$output = true,
$onlyUploadFile = false
$onlyUploadFile = false,
$whatIfFileExists = 'overwrite'
) {
$zip = new PclZip($uploaded_file['tmp_name']);
@ -1148,7 +1148,7 @@ function unzip_uploaded_document(
$destinationDir = api_get_path(SYS_ARCHIVE_PATH).$folder;
mkdir($destinationDir, api_get_permissions_for_new_directories(), true);
/* Uncompress zip file*/
// Uncompress zip file
// We extract using a callback function that "cleans" the path
$zip->extract(
PCLZIP_OPT_PATH,
@ -1168,7 +1168,8 @@ function unzip_uploaded_document(
$sessionId,
$groupId,
$output,
['path' => $uploadPath]
['path' => $uploadPath],
$whatIfFileExists
);
} else {
// Copy result
@ -1245,8 +1246,8 @@ function clean_up_path($path)
* The list of extensions accepted/rejected can be found from
* api_get_setting('upload_extensions_exclude') and api_get_setting('upload_extensions_include').
*
* @param string filename passed by reference. The filename will be modified
* if filter rules say so! (you can include path but the filename should look like 'abc.html')
* @param string $filename passed by reference. The filename will be modified
* if filter rules say so! (you can include path but the filename should look like 'abc.html')
*
* @return int 0 to skip file, 1 to keep file
*/
@ -1312,8 +1313,8 @@ function filter_extension(&$filename)
* @param int $readonly
* @param bool $saveVisibility
* @param int $group_id group.id
* @param int $session_id Session ID, if any
* @param int $userId creator id
* @param int $sessionId Session ID, if any
* @param int $userId creator user id
*
* @return int id if inserted document
*/
@ -1326,14 +1327,14 @@ function add_document(
$comment = null,
$readonly = 0,
$saveVisibility = true,
$group_id = null,
$session_id = 0,
$group_id = 0,
$sessionId = 0,
$userId = 0
) {
$session_id = empty($session_id) ? api_get_session_id() : $session_id;
$sessionId = empty($sessionId) ? api_get_session_id() : $sessionId;
$userId = empty($userId) ? api_get_user_id() : $userId;
$readonly = intval($readonly);
$readonly = (int) $readonly;
$c_id = $courseInfo['real_id'];
$params = [
'c_id' => $c_id,
@ -1343,7 +1344,7 @@ function add_document(
'title' => $title,
'comment' => $comment,
'readonly' => $readonly,
'session_id' => $session_id,
'session_id' => $sessionId,
];
$table = Database::get_course_table(TABLE_DOCUMENT);
$documentId = Database::insert($table, $params);
@ -1357,11 +1358,34 @@ function add_document(
TOOL_DOCUMENT,
$group_id,
$courseInfo,
$session_id,
$sessionId,
$userId
);
}
$allowNotification = api_get_configuration_value('send_notification_when_document_added');
if ($allowNotification) {
$courseTitle = $courseInfo['title'];
if (!empty($sessionId)) {
$sessionInfo = api_get_session_info($sessionId);
$courseTitle .= " ( ".$sessionInfo['name'].") ";
}
$url = api_get_path(WEB_CODE_PATH).
'document/showinframes.php?cidReq='.$courseInfo['code'].'&id_session='.$sessionId.'&id='.$documentId;
$link = Display::url(basename($title), $url, ['target' => '_blank']);
$userInfo = api_get_user_info($userId);
$message = sprintf(
get_lang('DocumentXHasBeenAddedToDocumentInYourCourseXByUserX'),
$link,
$courseTitle,
$userInfo['complete_name']
);
$subject = sprintf(get_lang('NewDocumentAddedToCourseX'), $courseTitle);
MessageManager::sendMessageToAllUsersInCourse($subject, $message, $courseInfo, $sessionId);
}
return $documentId;
} else {
return false;
@ -1898,13 +1922,15 @@ function build_missing_files_form($missing_files, $upload_path, $file_name)
*
* @param array $courseInfo
* @param array $userInfo
* @param string $base_work_dir
* @param string $folderPath
* @param string $base_work_dir course document dir
* @param string $folderPath folder to read
* @param int $sessionId
* @param int $groupId group.id
* @param int $groupId group.id
* @param bool $output
* @param array $parent
* @param string $uploadPath
* @param string $whatIfFileExists
*
* @return bool
*/
function add_all_documents_in_folder_to_database(
$courseInfo,
@ -1914,7 +1940,8 @@ function add_all_documents_in_folder_to_database(
$sessionId = 0,
$groupId = 0,
$output = false,
$parent = []
$parent = [],
$whatIfFileExists = 'overwrite'
) {
if (empty($userInfo) || empty($courseInfo)) {
return false;
@ -1932,12 +1959,11 @@ function add_all_documents_in_folder_to_database(
continue;
}
$parentPath = null;
$parentPath = '';
if (!empty($parent) && isset($parent['path'])) {
$parentPath = $parent['path'];
if ($parentPath == '/') {
$parentPath = null;
$parentPath = '';
}
}
@ -1954,14 +1980,52 @@ function add_all_documents_in_folder_to_database(
);
if ($folderExists === true) {
$documentId = DocumentManager::get_document_id($courseInfo, $completePath, $sessionId);
if ($documentId) {
$newFolderData = DocumentManager::get_document_data_by_id(
$documentId,
$courseInfo['code'],
false,
$sessionId
);
switch ($whatIfFileExists) {
case 'overwrite':
$documentId = DocumentManager::get_document_id($courseInfo, $completePath, $sessionId);
if ($documentId) {
$newFolderData = DocumentManager::get_document_data_by_id(
$documentId,
$courseInfo['code'],
false,
$sessionId
);
}
break;
case 'rename':
$newFolderData = create_unexisting_directory(
$courseInfo,
$userId,
$sessionId,
$groupId,
null,
$base_work_dir,
$completePath,
null,
null,
true
);
break;
case 'nothing':
if ($output) {
$documentId = DocumentManager::get_document_id($courseInfo, $completePath, $sessionId);
if ($documentId) {
$folderData = DocumentManager::get_document_data_by_id(
$documentId,
$courseInfo['code'],
false,
$sessionId
);
Display::addFlash(
Display::return_message(
$folderData['path'].' '.get_lang('UplAlreadyExists'),
'warning'
)
);
}
}
continue 2;
break;
}
} else {
$newFolderData = create_unexisting_directory(
@ -1987,7 +2051,8 @@ function add_all_documents_in_folder_to_database(
$sessionId,
$groupId,
$output,
$newFolderData
$newFolderData,
$whatIfFileExists
);
} else {
// Rename
@ -2009,7 +2074,7 @@ function add_all_documents_in_folder_to_database(
$groupId,
null,
0,
'overwrite',
$whatIfFileExists,
$output,
false,
null,

@ -400,19 +400,14 @@ class GlossaryManager
Display::return_icon('new_glossary_term.png', get_lang('TermAddNew'), '', ICON_SIZE_MEDIUM).'</a>';
}
if (!api_is_anonymous()) {
$actionsLeft .= '<a href="index.php?'.api_get_cidreq().'&action=export">'.
Display::return_icon('export_csv.png', get_lang('ExportGlossaryAsCSV'), '', ICON_SIZE_MEDIUM).'</a>';
}
if (api_is_allowed_to_edit(null, true)) {
$actionsLeft .= '<a href="index.php?'.api_get_cidreq().'&action=import">'.
Display::return_icon('import_csv.png', get_lang('ImportGlossary'), '', ICON_SIZE_MEDIUM).'</a>';
Display::return_icon('import.png', get_lang('ImportGlossary'), '', ICON_SIZE_MEDIUM).'</a>';
}
if (!api_is_anonymous()) {
$actionsLeft .= '<a href="index.php?'.api_get_cidreq().'&action=export_to_pdf">'.
Display::return_icon('pdf.png', get_lang('ExportToPDF'), '', ICON_SIZE_MEDIUM).'</a>';
$actionsLeft .= '<a id="export_opener" href="'.api_get_self().'?'.api_get_cidreq().'&action=export">'.
Display::return_icon('save.png', get_lang('Export'), '', ICON_SIZE_MEDIUM).'</a>';
}
if (($view == 'table') || (!isset($view))) {
@ -753,6 +748,7 @@ class GlossaryManager
$template = new Template('', false, false, false, true, false, false);
$layout = $template->get_template('glossary/export_pdf.tpl');
$template->assign('items', $data);
$html = $template->fetch($layout);
$courseCode = api_get_course_id();
$pdf = new PDF();
@ -804,4 +800,46 @@ class GlossaryManager
return false;
}
/**
* @param string $format
*/
public static function exportToFormat($format)
{
if ($format == 'pdf') {
self::export_to_pdf();
return;
}
$data = GlossaryManager::get_glossary_data(
0,
GlossaryManager::get_number_glossary_terms(api_get_session_id()),
0,
'ASC'
);
usort($data, 'sorter');
$list = [];
$list[] = ['term', 'definition'];
$allowStrip = api_get_configuration_value('allow_remove_tags_in_glossary_export');
foreach ($data as $line) {
$definition = $line[1];
if ($allowStrip) {
// htmlspecialchars_decode replace &#39 to '
// strip_tags remove HTML tags
$definition = htmlspecialchars_decode(strip_tags($definition), ENT_QUOTES);
}
$list[] = [$line[0], $definition];
}
$filename = 'glossary_course_'.api_get_course_id();
switch ($format) {
case 'csv':
Export::arrayToCsv($list, $filename);
break;
case 'xls':
Export::arrayToXls($list, $filename);
break;
}
}
}

@ -65,27 +65,37 @@ class Image
}
}
/**
* @param $cropParameters
*
* @return bool
*/
public function crop($cropParameters)
{
$image_size = $this->get_image_size($this->image_wrapper->path);
$src_width = $image_size['width'];
$src_height = $image_size['height'];
$cropParameters = explode(",", $cropParameters);
$x = intval($cropParameters[0]);
$y = intval($cropParameters[1]);
$width = intval($cropParameters[2]);
$height = intval($cropParameters[3]);
$image = $this->image_wrapper->crop(
$x,
$y,
$width,
$height,
$src_width,
$src_height
);
$cropParameters = explode(',', $cropParameters);
if (isset($cropParameters[0]) && isset($cropParameters[1])) {
$x = intval($cropParameters[0]);
$y = intval($cropParameters[1]);
$width = intval($cropParameters[2]);
$height = intval($cropParameters[3]);
$image = $this->image_wrapper->crop(
$x,
$y,
$width,
$height,
$src_width,
$src_height
);
return $image;
}
return $image;
return false;
}
/**
@ -278,6 +288,8 @@ class ImagickWrapper extends ImageWrapper
* @param int $height the height of the crop
* @param int $src_width the source width of the original image
* @param int $src_height the source height of the original image
*
* @return bool
*/
public function crop($x, $y, $width, $height, $src_width, $src_height)
{
@ -287,6 +299,8 @@ class ImagickWrapper extends ImageWrapper
$this->image->cropimage($width, $height, $x, $y);
$this->width = $width;
$this->height = $height;
return true;
}
public function send_image(

@ -1,9 +1,8 @@
<?php
/* For licensing terms, see /license.txt */
use Ddeboer\DataImport\Reader\CsvReader;
use Ddeboer\DataImport\Workflow;
use Ddeboer\DataImport\Writer\ArrayWriter;
use Ddeboer\DataImport\Reader\ExcelReader;
use League\Csv\Reader;
/**
* Class Import
@ -18,22 +17,11 @@ class Import
* @param string $path
* @param bool $setFirstRowAsHeader
*
* @return CsvReader
* @return array
*/
public static function csv_reader($path, $setFirstRowAsHeader = true)
{
if (empty($path)) {
return false;
}
$file = new \SplFileObject($path);
$csvReader = new CsvReader($file, ';');
if ($setFirstRowAsHeader) {
$csvReader->setHeaderRowNumber(0);
}
return $csvReader;
return self::csvToArray($path);
}
/**
@ -57,14 +45,44 @@ class Import
*/
public static function csvToArray($filename)
{
$csvReader = self::csv_reader($filename);
$resultArray = [];
if ($csvReader) {
$workflow = new Workflow\StepAggregator($csvReader);
$writer = new ArrayWriter($resultArray);
$workflow->addWriter($writer)->process();
if (empty($filename)) {
return [];
}
$reader = Reader::createFromPath($filename, 'r');
if ($reader) {
$reader->setDelimiter(';');
$reader->stripBom(true);
/*$contents = $reader->__toString();
if (!Utf8::isUtf8($contents)) {
// If file is not in utf8 try converting to ISO-8859-15
if ($reader->getStreamFilterMode() == 1) {
$reader->appendStreamFilter('convert.iconv.ISO-8859-15/UTF-8');
}
}*/
$iterator = $reader->fetchAssoc(0);
return iterator_to_array($iterator);
}
return [];
}
/**
* @param string $filename
*
* @return array
*/
public static function xlsToArray($filename)
{
if (empty($filename)) {
return [];
}
return $resultArray;
$file = new \SplFileObject($filename);
$reader = new ExcelReader($file, 0);
return $reader;
}
}

@ -347,8 +347,8 @@ function api_get_timezone()
* Returns the given date as a DATETIME in UTC timezone.
* This function should be used before entering any date in the DB.
*
* @param mixed $time The date to be converted (can be a string supported by date() or a timestamp)
* @param bool $return_null_if_invalid_date if the date is not correct return null instead of the current date
* @param mixed $time date to be converted (can be a string supported by date() or a timestamp)
* @param bool $returnNullIfInvalidDate if the date is not correct return null instead of the current date
* @param bool $returnObj
*
* @return string The DATETIME in UTC to be inserted in the DB,
@ -359,16 +359,15 @@ function api_get_timezone()
*/
function api_get_utc_datetime(
$time = null,
$return_null_if_invalid_date = false,
$returnNullIfInvalidDate = false,
$returnObj = false
) {
$to_timezone = 'UTC';
if (is_null($time) || empty($time) || $time === '0000-00-00 00:00:00') {
if ($return_null_if_invalid_date) {
if ($returnNullIfInvalidDate) {
return null;
}
if ($returnObj) {
return $date = new DateTime(gmdate('Y-m-d H:i:s'));
return $date = new DateTime(gmdate('Y-m-d H:i:s'), new DateTimeZone('UTC'));
}
return gmdate('Y-m-d H:i:s');
@ -376,15 +375,14 @@ function api_get_utc_datetime(
// If time is a timestamp, return directly in utc
if (is_numeric($time)) {
$time = intval($time);
$time = (int) $time;
return gmdate('Y-m-d H:i:s', $time);
}
try {
$fromTimezone = api_get_timezone();
$date = new DateTime($time, new DateTimezone($fromTimezone));
$date->setTimezone(new DateTimeZone($to_timezone));
$date->setTimezone(new DateTimeZone('UTC'));
if ($returnObj) {
return $date;
} else {
@ -945,11 +943,6 @@ function api_is_western_name_order($format = null, $language = null)
*/
function api_sort_by_first_name($language = null)
{
$userNameSortBy = api_get_setting('user_name_sort_by');
if (!empty($userNameSortBy) && in_array($userNameSortBy, ['firstname', 'lastname'])) {
return $userNameSortBy == 'firstname' ? true : false;
}
static $sort_by_first_name = [];
if (empty($language)) {

@ -12,7 +12,12 @@ if (api_get_setting('more_buttons_maximized_mode') === 'true') {
$template = new Template();
$template->setCSSEditor();
$template->assign('moreButtonsInMaximizedMode', $moreButtonsInMaximizedMode);
$template->assign('course_condition', api_get_cidreq());
$courseId = api_get_course_int_id();
$courseCondition = '';
if (!empty($courseId)) {
$courseCondition = api_get_cidreq();
}
$template->assign('course_condition', $courseCondition);
header('Content-type: application/x-javascript');
$template->display($template->get_template('javascript/editor/ckeditor/config_js.tpl'));

@ -1,372 +1,354 @@
CKEDITOR.dialog.add( 'video', function ( editor )
{
var lang = editor.lang.video;
var lang = editor.lang.video;
function commitValue( videoNode, extraStyles )
{
var value=this.getValue();
function commitValue( videoNode, extraStyles )
{
var value=this.getValue();
if ( !value && this.id=='id' )
value = generateId();
if ( !value && this.id=='id' )
value = generateId();
if (value == '') {
return;
}
videoNode.setAttribute( this.id, value);
videoNode.setAttribute( this.id, value);
if ( !value )
return;
switch( this.id )
{
case 'poster':
extraStyles.backgroundImage = 'url(' + value + ')';
break;
case 'width':
extraStyles.width = value + 'px';
break;
case 'height':
extraStyles.height = value + 'px';
break;
}
}
if ( !value )
return;
switch( this.id )
{
case 'poster':
extraStyles.backgroundImage = 'url(' + value + ')';
break;
case 'width':
extraStyles.width = value + 'px';
break;
case 'height':
extraStyles.height = value + 'px';
break;
}
}
function commitSrc( videoNode, extraStyles, videos )
{
var match = this.id.match(/(\w+)(\d)/),
id = match[1],
number = parseInt(match[2], 10);
function commitSrc( videoNode, extraStyles, videos )
{
var match = this.id.match(/(\w+)(\d)/),
id = match[1],
number = parseInt(match[2], 10);
var video = videos[number] || (videos[number]={});
video[id] = this.getValue();
}
var video = videos[number] || (videos[number]={});
video[id] = this.getValue();
}
function loadValue( videoNode )
{
if ( videoNode )
this.setValue( videoNode.getAttribute( this.id ) );
else
{
if ( this.id == 'id')
this.setValue( generateId() );
}
}
function loadValue( videoNode )
{
if ( videoNode )
this.setValue( videoNode.getAttribute( this.id ) );
else
{
if ( this.id == 'id')
this.setValue( generateId() );
}
}
function loadSrc( videoNode, videos )
{
var match = this.id.match(/(\w+)(\d)/),
id = match[1],
number = parseInt(match[2], 10);
function loadSrc( videoNode, videos )
{
var match = this.id.match(/(\w+)(\d)/),
id = match[1],
number = parseInt(match[2], 10);
var video = videos[number];
if (!video)
return;
this.setValue( video[ id ] );
}
var video = videos[number];
if (!video)
return;
this.setValue( video[ id ] );
}
function generateId()
{
var now = new Date();
return 'video' + now.getFullYear() + now.getMonth() + now.getDate() + now.getHours() + now.getMinutes() + now.getSeconds();
}
function generateId()
{
var now = new Date();
return 'video' + now.getFullYear() + now.getMonth() + now.getDate() + now.getHours() + now.getMinutes() + now.getSeconds();
}
// To automatically get the dimensions of the poster image
var onImgLoadEvent = function()
{
// Image is ready.
var preview = this.previewImage;
preview.removeListener( 'load', onImgLoadEvent );
preview.removeListener( 'error', onImgLoadErrorEvent );
preview.removeListener( 'abort', onImgLoadErrorEvent );
// To automatically get the dimensions of the poster image
var onImgLoadEvent = function()
{
// Image is ready.
var preview = this.previewImage;
preview.removeListener( 'load', onImgLoadEvent );
preview.removeListener( 'error', onImgLoadErrorEvent );
preview.removeListener( 'abort', onImgLoadErrorEvent );
this.setValueOf( 'info', 'width', preview.$.width );
this.setValueOf( 'info', 'height', preview.$.height );
};
this.setValueOf( 'info', 'width', preview.$.width );
this.setValueOf( 'info', 'height', preview.$.height );
};
var onImgLoadErrorEvent = function()
{
// Error. Image is not loaded.
var preview = this.previewImage;
preview.removeListener( 'load', onImgLoadEvent );
preview.removeListener( 'error', onImgLoadErrorEvent );
preview.removeListener( 'abort', onImgLoadErrorEvent );
};
var onImgLoadErrorEvent = function()
{
// Error. Image is not loaded.
var preview = this.previewImage;
preview.removeListener( 'load', onImgLoadEvent );
preview.removeListener( 'error', onImgLoadErrorEvent );
preview.removeListener( 'abort', onImgLoadErrorEvent );
};
return {
title : lang.dialogTitle,
minWidth : 400,
minHeight : 200,
return {
title : lang.dialogTitle,
minWidth : 400,
minHeight : 200,
onShow : function()
{
// Clear previously saved elements.
this.fakeImage = this.videoNode = null;
// To get dimensions of poster image
this.previewImage = editor.document.createElement( 'img' );
onShow : function()
{
// Clear previously saved elements.
this.fakeImage = this.videoNode = null;
// To get dimensions of poster image
this.previewImage = editor.document.createElement( 'img' );
var fakeImage = this.getSelectedElement();
if ( fakeImage && fakeImage.data( 'cke-real-element-type' ) && fakeImage.data( 'cke-real-element-type' ) == 'video' )
{
this.fakeImage = fakeImage;
var fakeImage = this.getSelectedElement();
if ( fakeImage && fakeImage.data( 'cke-real-element-type' ) && fakeImage.data( 'cke-real-element-type' ) == 'video' )
{
this.fakeImage = fakeImage;
var videoNode = editor.restoreRealElement( fakeImage ),
videos = [],
sourceList = videoNode.getElementsByTag( 'source', '' );
if (sourceList.count()==0)
sourceList = videoNode.getElementsByTag( 'source', 'cke' );
var videoNode = editor.restoreRealElement( fakeImage ),
videos = [],
sourceList = videoNode.getElementsByTag( 'source', '' );
if (sourceList.count()==0)
sourceList = videoNode.getElementsByTag( 'source', 'cke' );
for ( var i = 0, length = sourceList.count() ; i < length ; i++ )
{
var item = sourceList.getItem( i );
videos.push( {src : item.getAttribute( 'src' ), type: item.getAttribute( 'type' )} );
}
for ( var i = 0, length = sourceList.count() ; i < length ; i++ )
{
var item = sourceList.getItem( i );
videos.push( {src : item.getAttribute( 'src' ), type: item.getAttribute( 'type' )} );
}
this.videoNode = videoNode;
this.videoNode = videoNode;
this.setupContent( videoNode, videos );
}
else
this.setupContent( null, [] );
},
this.setupContent( videoNode, videos );
}
else
this.setupContent( null, [] );
},
onOk : function()
{
// If there's no selected element create one. Otherwise, reuse it
var videoNode = null;
if ( !this.fakeImage )
{
videoNode = CKEDITOR.dom.element.createFromHtml( '<cke:video></cke:video>', editor.document );
videoNode.setAttributes(
{
controls : 'controls'
} );
}
else
{
videoNode = this.videoNode;
}
onOk : function()
{
// If there's no selected element create one. Otherwise, reuse it
var videoNode = null;
if ( !this.fakeImage )
{
videoNode = CKEDITOR.dom.element.createFromHtml( '<cke:video></cke:video>', editor.document );
videoNode.setAttributes(
{
controls : 'controls'
} );
}
else
{
videoNode = this.videoNode;
}
var extraStyles = {}, videos = [];
this.commitContent( videoNode, extraStyles, videos );
var extraStyles = {}, videos = [];
this.commitContent( videoNode, extraStyles, videos );
var innerHtml = '', links = '',
link = lang.linkTemplate || '',
fallbackTemplate = lang.fallbackTemplate || '';
for(var i=0; i<videos.length; i++)
{
var video = videos[i];
if ( !video || !video.src ) { continue; }
//local copy of video URL
var mySrc = video.src;
//Chrome is picky about the redirect, so point directly to app/courses/ if currently pointing to courses/
//See https://support.chamilo.org/issues/8462
var coursesPathIndex = mySrc.indexOf('courses');
var newHtmlTag = '<cke:source src="' + mySrc + '" type="' + video.type + '" />';
var newLinks = link.replace('%src%', mySrc).replace('%type%', video.type);
//is video saved on courses folder ?
if (coursesPathIndex >= 0) {
//test if real path is not already present (in case of video edition)
var myPath = mySrc.indexOf('app');
if (myPath==-1) {
//add real path (app/) to video.src...
var myChromeSrc = mySrc.slice(0, coursesPathIndex) + "app/" + mySrc.slice(coursesPathIndex);
//insert full path link...
newHtmlTag = '<cke:source src="' + myChromeSrc + '" type="' + video.type + '" />';
newLinks = link.replace('%src%', myChromeSrc).replace('%type%', video.type);
}
}
innerHtml += newHtmlTag;
links += newLinks;
}
videoNode.setHtml( innerHtml + fallbackTemplate.replace( '%links%', links ) );
var innerHtml = '', links = '',
link = lang.linkTemplate || '',
fallbackTemplate = lang.fallbackTemplate || '';
for(var i=0; i<videos.length; i++)
{
var video = videos[i];
if ( !video || !video.src )
continue;
innerHtml += '<cke:source src="' + video.src + '" type="' + video.type + '" />';
links += link.replace('%src%', video.src).replace('%type%', video.type);
}
videoNode.setHtml( innerHtml + fallbackTemplate.replace( '%links%', links ) );
// Refresh the fake image.
var newFakeImage = editor.createFakeElement( videoNode, 'cke_video', 'video', false );
newFakeImage.setStyles( extraStyles );
if ( this.fakeImage )
{
newFakeImage.replace( this.fakeImage );
editor.getSelection().selectElement( newFakeImage );
}
else
{
// Insert it in a div
var div = new CKEDITOR.dom.element( 'DIV', editor.document );
editor.insertElement( div );
div.append( newFakeImage );
}
},
onHide : function()
{
if ( this.previewImage )
{
this.previewImage.removeListener( 'load', onImgLoadEvent );
this.previewImage.removeListener( 'error', onImgLoadErrorEvent );
this.previewImage.removeListener( 'abort', onImgLoadErrorEvent );
this.previewImage.remove();
this.previewImage = null; // Dialog is closed.
}
},
// Refresh the fake image.
var newFakeImage = editor.createFakeElement( videoNode, 'cke_video', 'video', false );
newFakeImage.setStyles( extraStyles );
if ( this.fakeImage )
{
newFakeImage.replace( this.fakeImage );
editor.getSelection().selectElement( newFakeImage );
}
else
{
// Insert it in a div
var div = new CKEDITOR.dom.element( 'DIV', editor.document );
editor.insertElement( div );
div.append( newFakeImage );
}
},
onHide : function()
{
if ( this.previewImage )
{
this.previewImage.removeListener( 'load', onImgLoadEvent );
this.previewImage.removeListener( 'error', onImgLoadErrorEvent );
this.previewImage.removeListener( 'abort', onImgLoadErrorEvent );
this.previewImage.remove();
this.previewImage = null; // Dialog is closed.
}
},
contents :
[
{
id : 'info',
elements :
[
{
type : 'hbox',
widths: [ '33%', '33%', '33%'],
children : [
{
type : 'text',
id : 'width',
label : editor.lang.common.width,
'default' : 400,
validate : CKEDITOR.dialog.validate.notEmpty( lang.widthRequired ),
commit : commitValue,
setup : loadValue
},
{
type : 'text',
id : 'height',
label : editor.lang.common.height,
'default' : 300,
//validate : CKEDITOR.dialog.validate.notEmpty(lang.heightRequired ),
commit : commitValue,
setup : loadValue
},
{
type : 'text',
id : 'id',
label : 'Id',
commit : commitValue,
setup : loadValue
}
]
},
{
type : 'hbox',
widths: [ '', '100px', '75px'],
children : [
{
type : 'text',
id : 'src0',
label : lang.sourceVideo,
commit : commitSrc,
setup : loadSrc
},
{
type : 'button',
id : 'browse',
hidden : 'true',
style : 'display:inline-block;margin-top:10px;',
filebrowser :
{
action : 'Browse',
target: 'info:src0',
url: editor.config.filebrowserVideoBrowseUrl || editor.config.filebrowserBrowseUrl
},
label : editor.lang.common.browseServer
},
{
id : 'type0',
label : lang.sourceType,
type : 'select',
'default' : 'video/mp4',
items :
[
[ 'MP4', 'video/mp4' ],
[ 'WebM', 'video/webm' ]
],
commit : commitSrc,
setup : loadSrc
}]
},
contents :
[
{
id : 'info',
elements :
[
{
type : 'hbox',
widths: [ '33%', '33%', '33%'],
children : [
{
type : 'text',
id : 'width',
label : editor.lang.common.width,
'default' : 400,
validate : CKEDITOR.dialog.validate.notEmpty( lang.widthRequired ),
commit : commitValue,
setup : loadValue
},
{
type : 'text',
id : 'height',
label : editor.lang.common.height,
'default' : 300,
//validate : CKEDITOR.dialog.validate.notEmpty(lang.heightRequired ),
commit : commitValue,
setup : loadValue
},
{
type : 'text',
id : 'id',
label : 'Id',
commit : commitValue,
setup : loadValue
}
]
},
{
type : 'hbox',
widths: [ '', '100px', '75px'],
children : [
{
type : 'text',
id : 'src0',
label : lang.sourceVideo,
commit : commitSrc,
setup : loadSrc
},
{
type : 'button',
id : 'browse',
hidden : 'true',
style : 'display:inline-block;margin-top:10px;',
filebrowser :
{
action : 'Browse',
target: 'info:src0',
url: editor.config.filebrowserVideoBrowseUrl || editor.config.filebrowserBrowseUrl
},
label : editor.lang.common.browseServer
},
{
id : 'type0',
label : lang.sourceType,
type : 'select',
'default' : 'video/mp4',
items :
[
[ 'MP4', 'video/mp4' ],
[ 'WebM', 'video/webm' ]
],
commit : commitSrc,
setup : loadSrc
}]
},
{
type : 'hbox',
widths: [ '', '100px', '75px'],
children : [
{
type : 'text',
id : 'src1',
label : lang.sourceVideo,
commit : commitSrc,
setup : loadSrc
},
{
type : 'button',
id : 'browse',
hidden : 'true',
style : 'display:inline-block;margin-top:10px;',
filebrowser :
{
action : 'Browse',
target: 'info:src1',
url: editor.config.filebrowserVideoBrowseUrl || editor.config.filebrowserBrowseUrl
},
label : editor.lang.common.browseServer
},
{
id : 'type1',
label : lang.sourceType,
type : 'select',
'default':'video/webm',
items :
[
[ 'MP4', 'video/mp4' ],
[ 'WebM', 'video/webm' ]
],
commit : commitSrc,
setup : loadSrc
}]
},
{
type : 'hbox',
widths: [ '', '100px'],
children : [
{
type : 'text',
id : 'poster',
label : lang.poster,
commit : commitValue,
setup : loadValue,
onChange : function()
{
var dialog = this.getDialog(),
newUrl = this.getValue();
{
type : 'hbox',
widths: [ '', '100px', '75px'],
children : [
{
type : 'text',
id : 'src1',
label : lang.sourceVideo,
commit : commitSrc,
setup : loadSrc
},
{
type : 'button',
id : 'browse',
hidden : 'true',
style : 'display:inline-block;margin-top:10px;',
filebrowser :
{
action : 'Browse',
target: 'info:src1',
url: editor.config.filebrowserVideoBrowseUrl || editor.config.filebrowserBrowseUrl
},
label : editor.lang.common.browseServer
},
{
id : 'type1',
label : lang.sourceType,
type : 'select',
'default':'video/webm',
items :
[
[ 'MP4', 'video/mp4' ],
[ 'WebM', 'video/webm' ]
],
commit : commitSrc,
setup : loadSrc
}]
},
{
type : 'hbox',
widths: [ '', '100px'],
children : [
{
type : 'text',
id : 'poster',
label : lang.poster,
commit : commitValue,
setup : loadValue,
onChange : function()
{
var dialog = this.getDialog(),
newUrl = this.getValue();
//Update preview image
if ( newUrl.length > 0 ) //Prevent from load before onShow
{
dialog = this.getDialog();
var preview = dialog.previewImage;
//Update preview image
if ( newUrl.length > 0 ) //Prevent from load before onShow
{
dialog = this.getDialog();
var preview = dialog.previewImage;
preview.on( 'load', onImgLoadEvent, dialog );
preview.on( 'error', onImgLoadErrorEvent, dialog );
preview.on( 'abort', onImgLoadErrorEvent, dialog );
preview.setAttribute( 'src', newUrl );
}
}
},
{
type : 'button',
id : 'browse',
hidden : 'true',
style : 'display:inline-block;margin-top:10px;',
filebrowser :
{
action : 'Browse',
target: 'info:poster',
url: editor.config.filebrowserImageBrowseUrl || editor.config.filebrowserBrowseUrl
},
label : editor.lang.common.browseServer
}]
}
]
}
preview.on( 'load', onImgLoadEvent, dialog );
preview.on( 'error', onImgLoadErrorEvent, dialog );
preview.on( 'abort', onImgLoadErrorEvent, dialog );
preview.setAttribute( 'src', newUrl );
}
}
},
{
type : 'button',
id : 'browse',
hidden : 'true',
style : 'display:inline-block;margin-top:10px;',
filebrowser :
{
action : 'Browse',
target: 'info:poster',
url: editor.config.filebrowserImageBrowseUrl || editor.config.filebrowserBrowseUrl
},
label : editor.lang.common.browseServer
}]
}
]
}
]
};
} );
]
};
} );

@ -101,14 +101,12 @@ window.HotspotQuestion = (function () {
if (x >= startX) {
width = x - startX;
this.set('radiusX', Math.round(width / 2));
this.set('centerX', startX + this.get('radiusX'));
}
if (y >= startY) {
height = y - startY;
this.set('radiusY', Math.round(height / 2));
this.set('centerY', startY + this.get('radiusY'));
}
@ -258,12 +256,9 @@ window.HotspotQuestion = (function () {
var HotspotSVG = function (modelModel, index) {
var self = this;
this.model = modelModel;
this.hotspotIndex = index;
this.el = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
this.model.onChange(function (hotspotModel) {
self.render();
});
@ -368,7 +363,6 @@ window.HotspotQuestion = (function () {
</div>\n\
';
$el.html(template);
$el.find('select')
.on('change', function () {
selectedHotspotIndex = self.hotspotIndex;
@ -747,7 +741,6 @@ window.HotspotQuestion = (function () {
hotspotsSVG = new AdminHotspotsSVG(hotspotsCollection, this);
$(config.selector).css('width', this.width).append(hotspotsSVG.render().el);
$(config.selector).parent().prepend('\n\
<div id="hotspot-messages" class="alert alert-info">\n\
<h4><span class="fa fa-info-circle" aria-hidden="true"></span> ' + lang.HotspotStatus1 + '</h4>\n\

@ -22,7 +22,7 @@ $group_disk_path = api_get_path(SYS_COURSE_PATH).$course_info['path'].'/document
$group_web_path = api_get_path(WEB_COURSE_PATH).$course_info['path'].'/document'.$groupdirpath.'/';
//get all group files and folders
$docs_and_folders = DocumentManager::get_all_document_data(
$docs_and_folders = DocumentManager::getAllDocumentData(
$course_info,
$groupdirpath,
$groupIid,
@ -32,7 +32,7 @@ $docs_and_folders = DocumentManager::get_all_document_data(
);
//get all group filenames
$array_to_search = is_array($docs_and_folders) ? $docs_and_folders : array();
$array_to_search = !empty($docs_and_folders) ? $docs_and_folders : [];
if (count($array_to_search) > 0) {
while (list($key) = each($array_to_search)) {

@ -15,7 +15,7 @@ $curdirpath='/images/gallery'; //path of library directory
$course_info = api_get_course_info();
// Get all files and folders
$docs_and_folders = DocumentManager::get_all_document_data(
$docs_and_folders = DocumentManager::getAllDocumentData(
$course_info,
$curdirpath,
0,
@ -25,7 +25,7 @@ $docs_and_folders = DocumentManager::get_all_document_data(
);
//get all filenames
$array_to_search = is_array($docs_and_folders) ? $docs_and_folders : array();
$array_to_search = !empty($docs_and_folders) ? $docs_and_folders : [];
if (count($array_to_search) > 0) {
while (list($key) = each($array_to_search)) {

@ -215,7 +215,6 @@ class Login
*/
public static function sendResetEmail(User $user)
{
//if (null === $user->getConfirmationToken()) {
$uniqueId = api_get_unique_id();
$user->setConfirmationToken($uniqueId);
$user->setPasswordRequestedAt(new \DateTime());
@ -237,7 +236,6 @@ class Login
$mailBody
);
Display::addFlash(Display::return_message(get_lang('CheckYourEmailAndFollowInstructions')));
//}
}
/**

@ -513,7 +513,7 @@ class MessageManager
* @param bool $directMessage
* @param array $smsParameters
* @param bool $uploadFiles Do not upload files using the MessageManager class
* @param bool $attachmentList
* @param array $attachmentList
*
* @return bool
*/
@ -2250,8 +2250,7 @@ class MessageManager
while (!feof($mailFile)) {
$mailLine = fgets($mailFile);
//if ($iX == 4 && preg_match('/(.*):\s(.*)$/', $mailLine, $matches)) {
if (
$iX == 2 &&
if ($iX == 2 &&
preg_match('/(.*)(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})\s(.*)/', $mailLine, $detailsMatches)
) {
$mail_queue[$i]['reason'] = $detailsMatches[3];
@ -2279,8 +2278,6 @@ class MessageManager
/**
* @param int $userId
*
* @throws \Doctrine\DBAL\DBALException
*
* @return array
*/
public static function getUsersThatHadConversationWithUser($userId)
@ -2314,8 +2311,6 @@ class MessageManager
* @param int $userId
* @param int $otherUserId
*
* @throws \Doctrine\DBAL\DBALException
*
* @return array
*/
public static function getAllMessagesBetweenStudents($userId, $otherUserId)
@ -2345,6 +2340,49 @@ class MessageManager
return $list;
}
/**
* @param string $subject
* @param string $message
* @param array $courseInfo
* @param int $sessionId
*
* @return bool
*/
public static function sendMessageToAllUsersInCourse($subject, $message, $courseInfo, $sessionId = 0)
{
if (empty($courseInfo)) {
return false;
}
$senderId = api_get_user_id();
if (empty($senderId)) {
return false;
}
if (empty($sessionId)) {
// Course students and teachers
$users = CourseManager::get_user_list_from_course_code($courseInfo['code']);
} else {
// Course-session students and course session coaches
$users = CourseManager::get_user_list_from_course_code($courseInfo['code'], $sessionId);
}
if (empty($users)) {
return false;
}
foreach ($users as $userInfo) {
self::send_message_simple(
$userInfo['user_id'],
$subject,
$message,
$senderId,
false,
false,
[],
false
);
}
}
/**
* Execute the SQL necessary to know the number of messages in the database.
*

@ -54,14 +54,15 @@ class Model
*/
public function delete($id)
{
if (empty($id) or $id != strval(intval($id))) {
if (empty($id) || $id != strval(intval($id))) {
return false;
}
$params = ['id = ?' => $id];
if ($this->is_course_model) {
$course_id = api_get_course_int_id();
$params = ['id = ? AND c_id = ?' => [$id, $course_id]];
$courseId = api_get_course_int_id();
$params = ['id = ? AND c_id = ?' => [$id, $courseId]];
}
// Database table definition
$result = Database::delete($this->table, $params);
if ($result != 1) {

@ -114,7 +114,7 @@ class NotebookManager
}
// Database table definition
$t_notebook = Database::get_course_table(TABLE_NOTEBOOK);
$table = Database::get_course_table(TABLE_NOTEBOOK);
$course_id = api_get_course_int_id();
$sql = "SELECT
@ -122,7 +122,7 @@ class NotebookManager
title AS note_title,
description AS note_comment,
session_id AS session_id
FROM $t_notebook
FROM $table
WHERE c_id = $course_id AND notebook_id = '".intval($notebook_id)."' ";
$result = Database::query($sql);
if (Database::num_rows($result) != 1) {
@ -199,11 +199,11 @@ class NotebookManager
}
// Database table definition
$t_notebook = Database::get_course_table(TABLE_NOTEBOOK);
$table = Database::get_course_table(TABLE_NOTEBOOK);
$course_id = api_get_course_int_id();
$sql = "DELETE FROM $t_notebook
$sql = "DELETE FROM $table
WHERE
c_id = $course_id AND
notebook_id='".intval($notebook_id)."' AND
@ -231,6 +231,7 @@ class NotebookManager
*/
public static function display_notes()
{
$sessionId = api_get_session_id();
$_user = api_get_user_info();
if (!isset($_GET['direction'])) {
$sort_direction = 'ASC';
@ -246,15 +247,7 @@ class NotebookManager
// action links
echo '<div class="actions">';
if (!api_is_anonymous()) {
if (api_get_session_id() == 0) {
echo '<a href="index.php?'.api_get_cidreq().'&action=addnote">'.
Display::return_icon(
'new_note.png',
get_lang('NoteAddNew'),
'',
'32'
).'</a>';
} elseif (api_is_allowed_to_session_edit(false, true)) {
if ($sessionId == 0 || api_is_allowed_to_session_edit(false, true)) {
echo '<a href="index.php?'.api_get_cidreq().'&action=addnote">'.
Display::return_icon('new_note.png', get_lang('NoteAddNew'), '', '32').'</a>';
}
@ -278,17 +271,16 @@ class NotebookManager
}
// Database table definition
$t_notebook = Database::get_course_table(TABLE_NOTEBOOK);
$table = Database::get_course_table(TABLE_NOTEBOOK);
$order_by = " ORDER BY ".$notebookView." $sort_direction ";
// Condition for the session
$session_id = api_get_session_id();
$condition_session = api_get_session_condition($session_id);
$condition_session = api_get_session_condition($sessionId);
$cond_extra = $notebookView == 'update_date' ? " AND update_date <> ''" : " ";
$course_id = api_get_course_int_id();
$sql = "SELECT * FROM $t_notebook
$sql = "SELECT * FROM $table
WHERE
c_id = $course_id AND
user_id = '".api_get_user_id()."'

@ -10,6 +10,7 @@ use Chamilo\CoreBundle\Component\Utils\ChamiloApi;
*/
class PDF
{
/** @var mPDF */
public $pdf;
public $custom_header = [];
public $custom_footer = [];
@ -32,9 +33,7 @@ class PDF
$template = null
) {
$this->template = $template;
/* More info @ http://mpdf1.com/manual/index.php?tid=184&searchstring=mPDF
* mPDF ([ string $mode [, mixed $format [, float $default_font_size [, string $default_font [, float $margin_left , float $margin_right , float $margin_top , float $margin_bottom , float $margin_header , float $margin_footer [, string $orientation ]]]]]])
*/
/* More info @ http://mpdf1.com/manual/index.php?tid=184&searchstring=mPDF */
if (!in_array($orientation, ['P', 'L'])) {
$orientation = 'P';
}
@ -311,7 +310,8 @@ class PDF
$document_html = self::fixImagesPaths($document_html, $course_data, $dirName);
api_set_encoding_html($document_html, 'UTF-8'); // The library mPDF expects UTF-8 encoded input data.
// The library mPDF expects UTF-8 encoded input data.
api_set_encoding_html($document_html, 'UTF-8');
// TODO: Maybe it is better idea the title to be passed through
$title = api_get_title_html($document_html, 'UTF-8', 'UTF-8');
// $_GET[] too, as it is done with file name.
@ -395,7 +395,6 @@ class PDF
//absolute path for frames.css //TODO: necessary?
$absolute_css_path = api_get_path(WEB_CSS_PATH).api_get_setting('stylesheets').'/frames.css';
$document_html = str_replace('href="./css/frames.css"', 'href="'.$absolute_css_path.'"', $document_html);
$document_html = str_replace('../../', '', $document_html);
$document_html = str_replace('../', '', $document_html);
$document_html = str_replace(
@ -419,8 +418,16 @@ class PDF
if (strpos($old_src, $protocol) === false) {
if (strpos($old_src, '/main/default_course_document') === false) {
if (strpos($old_src, '/main/inc/lib/') === false) {
$old_src_fixed = str_replace(api_get_path(REL_COURSE_PATH).$course_data['path'].'/document/', '', $old_src);
$old_src_fixed = str_replace('courses/'.$course_data['path'].'/document/', '', $old_src_fixed);
$old_src_fixed = str_replace(
api_get_path(REL_COURSE_PATH).$course_data['path'].'/document/',
'',
$old_src
);
$old_src_fixed = str_replace(
'courses/'.$course_data['path'].'/document/',
'',
$old_src_fixed
);
$new_path = $document_path.$old_src_fixed;
$document_html = str_replace($old_src, $new_path, $document_html);
}
@ -438,22 +445,9 @@ class PDF
);
$document_html = str_replace(api_get_path(WEB_ARCHIVE_PATH), api_get_path(SYS_ARCHIVE_PATH), $document_html);
//replace relative path by absolute path for resources
//$document_html= str_replace('src="/chamilo/main/default_course_document/', 'temp_template_path', $document_html);// before save src templates not apply
//$document_html= str_replace('src="/', 'temp_template_path', $document_html);// before save src templates not apply
//$document_html= str_replace('src="/chamilo/main/default_course_document/', 'temp_template_path', $document_html);// before save src templates not apply
//$src_http_www= 'src="'.api_get_path(WEB_COURSE_PATH).$course_data['path'].'/document/';
//$document_html= str_replace('src="',$src_http_www, $document_html);
//$document_html= str_replace('temp_template_path', 'src="/main/default_course_document/', $document_html);// restore src templates
// The library mPDF expects UTF-8 encoded input data.
api_set_encoding_html($document_html, 'UTF-8');
// TODO: Maybe it is better idea the title to be passed through
$title = api_get_title_html($document_html, 'UTF-8', 'UTF-8');
// $_GET[] too, as it is done with file name.
// At the moment the title is retrieved from the html document itself.
if ($returnHtml) {
return "<style>$css</style>".$document_html;
}
@ -641,7 +635,6 @@ class PDF
$this->pdf->defaultheaderline = 1; // 1 to include line below header/above footer
$userId = api_get_user_id();
if (!empty($course_data['code'])) {
$teacher_list = CourseManager::get_teacher_list_from_course_code($course_data['code']);
@ -726,7 +719,6 @@ class PDF
// Adding watermark
if (api_get_setting('pdf_export_watermark_enable') == 'true') {
$watermark_file = self::get_watermark($course_code);
if ($watermark_file) {
//http://mpdf1.com/manual/index.php?tid=269&searchstring=watermark
$this->pdf->SetWatermarkImage($watermark_file);

@ -57,7 +57,7 @@ class HTML_QuickForm_Rule_Compare extends HTML_QuickForm_Rule
* @param string operator name
* @return string operator to use for validation
*/
function _findOperator($name)
public function _findOperator($name)
{
$name = trim($name);
if (empty($name)) {
@ -79,12 +79,38 @@ class HTML_QuickForm_Rule_Compare extends HTML_QuickForm_Rule
public function validate($values, $operator = null)
{
$operator = $this->_findOperator($operator);
$a = $values[0];
$b = $values[1];
if ('===' != $operator && '!==' != $operator) {
$compareFn = create_function('$a, $b', 'return floatval($a) ' . $operator . ' floatval($b);');
$a = floatval($a);
$b = floatval($b);
} else {
$compareFn = create_function('$a, $b', 'return strval($a) ' . $operator . ' strval($b);');
$a = strval($a);
$b = strval($b);
}
switch ($operator) {
case '===':
return $a === $b;
break;
case '!==':
return $a !== $b;
break;
case '>':
return $a > $b;
break;
case '>=':
return $a >= $b;
break;
case '<':
return $a < $b;
break;
case '<=':
return $a <= $b;
break;
}
return $compareFn($values[0], $values[1]);
}
public function getValidationScript($operator = null)

@ -714,7 +714,7 @@ class HTML_QuickForm_advmultiselect extends HTML_QuickForm_select
$attrHidden = $this->_getAttrString($this->_attributesHidden);
// prepare option tables to be displayed as in POST order
$append = count($this->_values);
$append = empty($this->_values) ? 0 : count($this->_values);
if ($append > 0) {
$arrHtmlSelected = array_fill(0, $append, ' ');
} else {

@ -333,7 +333,7 @@ class HTML_QuickForm_file extends HTML_QuickForm_input
dataUrl = canvas.toDataURL();
$image.attr(\'src\', dataUrl).cropper(\'destroy\').off(\'load\', imageCropper);
$(\'[name="'.$id.'_crop_image_base_64]"\').val(dataUrl);
$(\'[name="'.$id.'_crop_image_base_64"]\').val(dataUrl);
$cropButton.hide();
});
});

@ -53,7 +53,7 @@ class HTML_QuickForm_select extends HTML_QuickForm_element
* @since 1.0
* @access private
*/
protected $_values;
protected $_values = [];
/**
* Class constructor

@ -90,6 +90,23 @@ class SessionManager
'send_subscription_notification' => $session->getSendSubscriptionNotification(),
];
// Converted to local values
$variables = [
'display_start_date',
'display_end_date',
'access_start_date',
'access_end_date',
'coach_access_start_date',
'coach_access_end_date',
];
foreach ($variables as $value) {
$result[$value."_to_local_time"] = null;
if (!empty($result[$value])) {
$result[$value."_to_local_time"] = api_get_local_time($result[$value]);
}
}
return $result;
}
@ -106,7 +123,8 @@ class SessionManager
* @param string $coachStartDate (YYYY-MM-DD hh:mm:ss)
* @param string $coachEndDate (YYYY-MM-DD hh:mm:ss)
* @param int $sessionCategoryId ID of the session category in which this session is registered
* @param mixed $coachId If integer, this is the session coach id, if string, the coach ID will be looked for from the user table
* @param mixed $coachId If int, this is the session coach id,
* if string, the coach ID will be looked for from the user table
* @param int $visibility Visibility after end date (0 = read-only, 1 = invisible, 2 = accessible)
* @param bool $fixSessionNameIfExists
* @param string $duration
@ -142,7 +160,7 @@ class SessionManager
) {
global $_configuration;
//Check portal limits
// Check portal limits
$access_url_id = 1;
if (api_get_multiple_access_url()) {
@ -173,11 +191,15 @@ class SessionManager
$msg = get_lang('SessionNameIsRequired');
return $msg;
} elseif (!empty($startDate) && !api_is_valid_date($startDate, 'Y-m-d H:i') && !api_is_valid_date($startDate, 'Y-m-d H:i:s')) {
} elseif (!empty($startDate) && !api_is_valid_date($startDate, 'Y-m-d H:i') &&
!api_is_valid_date($startDate, 'Y-m-d H:i:s')
) {
$msg = get_lang('InvalidStartDate');
return $msg;
} elseif (!empty($endDate) && !api_is_valid_date($endDate, 'Y-m-d H:i') && !api_is_valid_date($endDate, 'Y-m-d H:i:s')) {
} elseif (!empty($endDate) && !api_is_valid_date($endDate, 'Y-m-d H:i') &&
!api_is_valid_date($endDate, 'Y-m-d H:i:s')
) {
$msg = get_lang('InvalidEndDate');
return $msg;
@ -269,7 +291,6 @@ class SessionManager
if (!empty($session_id)) {
$extraFields['item_id'] = $session_id;
$sessionFieldValue = new ExtraFieldValue('session');
$sessionFieldValue->saveFieldValues($extraFields);
@ -2774,11 +2795,11 @@ class SessionManager
/**
* Update an extra field value for a given session.
*
* @param int Course ID
* @param string Field variable name
* @param string Field value
* @param int $sessionId Session ID
* @param string $variable Field variable name
* @param string $value Optional. Default field value
*
* @return bool true if field updated, false otherwise
* @return bool|int An integer when register a new extra field. And boolean when update the extrafield
*/
public static function update_session_extra_field_value($sessionId, $variable, $value = '')
{
@ -3164,10 +3185,10 @@ class SessionManager
*/
public static function get_session_category($id)
{
$tbl_session_category = Database::get_main_table(TABLE_MAIN_SESSION_CATEGORY);
$table = Database::get_main_table(TABLE_MAIN_SESSION_CATEGORY);
$id = intval($id);
$sql = "SELECT id, name, date_start, date_end
FROM $tbl_session_category
FROM $table
WHERE id= $id";
$result = Database::query($sql);
$num = Database::num_rows($result);
@ -3185,9 +3206,9 @@ class SessionManager
*/
public static function get_all_session_category()
{
$tbl_session_category = Database::get_main_table(TABLE_MAIN_SESSION_CATEGORY);
$table = Database::get_main_table(TABLE_MAIN_SESSION_CATEGORY);
$id = api_get_current_access_url_id();
$sql = 'SELECT * FROM '.$tbl_session_category.'
$sql = 'SELECT * FROM '.$table.'
WHERE access_url_id = '.$id.'
ORDER BY name ASC';
$result = Database::query($sql);
@ -3685,15 +3706,13 @@ class SessionManager
$row['access_end_date'] = null;
}
if (
$row['coach_access_start_date'] == '0000-00-00 00:00:00' ||
if ($row['coach_access_start_date'] == '0000-00-00 00:00:00' ||
$row['coach_access_start_date'] == '0000-00-00'
) {
$row['coach_access_start_date'] = null;
}
if (
$row['coach_access_end_date'] == '0000-00-00 00:00:00' ||
if ($row['coach_access_end_date'] == '0000-00-00 00:00:00' ||
$row['coach_access_end_date'] == '0000-00-00'
) {
$row['coach_access_end_date'] = null;
@ -4190,8 +4209,8 @@ class SessionManager
/**
* Updates a session status.
*
* @param int session id
* @param int status
* @param int session id
* @param int status
*/
public static function set_session_status($session_id, $status)
{
@ -4375,9 +4394,9 @@ class SessionManager
{
$session_id = intval($session_id);
$user_id = intval($user_id);
$session_table = Database::get_main_table(TABLE_MAIN_SESSION);
$table = Database::get_main_table(TABLE_MAIN_SESSION);
$sql = "SELECT DISTINCT id
FROM $session_table
FROM $table
WHERE session.id_coach = '".$user_id."' AND id = '$session_id'";
$result = Database::query($sql);
if ($result && Database::num_rows($result)) {
@ -4566,8 +4585,8 @@ class SessionManager
* false: if session exists a new session will be created adding a counter session1, session2, etc
* @param int $defaultUserId
* @param mixed $logger
* @param array $extraFields convert a file row to an extra field. Example in CSV file there's a SessionID then it will
* converted to extra_external_session_id if you set this: array('SessionId' => 'extra_external_session_id')
* @param array $extraFields convert a file row to an extra field. Example in CSV file there's a SessionID
* then it will converted to extra_external_session_id if you set: array('SessionId' => 'extra_external_session_id')
* @param string $extraFieldId
* @param int $daysCoachAccessBeforeBeginning
* @param int $daysCoachAccessAfterBeginning
@ -7603,7 +7622,7 @@ class SessionManager
$sessionList = [];
$sessionList[] = '';
foreach ($sessions as $session) {
$sessionList[$session['id']] = $session['name'];
$sessionList[$session['id']] = strip_tags($session['name']);
}
$form->addSelect(
@ -7659,7 +7678,7 @@ class SessionManager
'id' => 'access',
]);
$form->addHtml('<div id="duration" style="display:none">');
$form->addHtml('<div id="duration_div" style="display:none">');
$form->addElement(
'number',

@ -2,6 +2,8 @@
/* For licensing terms, see /license.txt */
use Chamilo\CoreBundle\Component\Utils\ChamiloApi;
use Chamilo\CoreBundle\Entity\Skill as SkillEntity;
use Chamilo\SkillBundle\Entity\SkillRelCourse;
use Chamilo\SkillBundle\Entity\SkillRelItem;
use Chamilo\UserBundle\Entity\Repository\UserRepository;
use Chamilo\UserBundle\Entity\User;
@ -1541,6 +1543,7 @@ class Skill extends Model
'id' => '1',
'name' => get_lang('Root'),
'parent_id' => '0',
'status' => 1,
];
$skillInfo = $this->getSkillInfo($skill_id);
@ -2444,7 +2447,7 @@ class Skill extends Model
* @param int $itemId
* @param int $userId
*/
public static function addSkillsToUserForm(FormValidator $form, $typeId, $itemId, $userId)
public static function addSkillsToUserForm(FormValidator $form, $typeId, $itemId, $userId, $resultId = 0, $addHeader = false)
{
$allowSkillInTools = api_get_configuration_value('allow_skill_rel_items');
if ($allowSkillInTools && !empty($typeId) && !empty($itemId) && !empty($userId)) {
@ -2478,8 +2481,12 @@ class Skill extends Model
'user_id' => $userId,
'course_id' => api_get_course_int_id(),
'session_id' => api_get_session_id(),
'result_id' => $resultId,
];
$params = json_encode($params);
if ($addHeader) {
$form->addHtml(Display::page_subheader2(get_lang('Skills')));
}
$html = '
<script>
$(function() {
@ -2501,6 +2508,9 @@ class Skill extends Model
';
$form->addHtml($html);
$form->addLabel(get_lang('Skills'), $skills);
if ($addHeader) {
$form->addHtml('<br />');
}
}
}
}
@ -2535,12 +2545,16 @@ class Skill extends Model
}
}
$courseId = api_get_course_int_id();
$sessionId = api_get_session_id();
$url = api_get_path(WEB_AJAX_PATH).'skill.ajax.php?a=search_skills_in_course&course_id='.$courseId.'&session_id='.$sessionId;
$form->addSelectAjax(
'skills',
get_lang('Skills'),
$skillList,
[
'url' => api_get_path(WEB_AJAX_PATH).'skill.ajax.php?a=search_skills',
'url' => $url,
'multiple' => 'multiple',
]
);
@ -2756,7 +2770,7 @@ class Skill extends Model
// Add new one
if (!empty($skills)) {
foreach ($skills as $skillId) {
/** @var \Chamilo\CoreBundle\Entity\Skill $skill */
/** @var SkillEntity $skill */
$skill = $em->getRepository('ChamiloCoreBundle:Skill')->find($skillId);
if ($skill) {
if (!$skill->hasItem($typeId, $itemId)) {
@ -2778,4 +2792,89 @@ class Skill extends Model
}
}
}
/**
* Relate skill with an item (exercise, gradebook, lp, etc).
*
* @param FormValidator $form
*
* @return bool
*/
public static function saveSkillsToCourseFromForm(FormValidator $form)
{
$skills = (array) $form->getSubmitValue('skills');
$courseId = (int) $form->getSubmitValue('course_id');
$sessionId = $form->getSubmitValue('session_id');
return self::saveSkillsToCourse($skills, $courseId, $sessionId);
}
/**
* @param array $skills
* @param int $courseId
* @param int $sessionId
*
* @throws \Doctrine\ORM\OptimisticLockException
*
* @return bool
*/
public static function saveSkillsToCourse($skills, $courseId, $sessionId)
{
$allowSkillInTools = api_get_configuration_value('allow_skill_rel_items');
if (!$allowSkillInTools) {
return false;
}
$em = Database::getManager();
$sessionId = empty($sessionId) ? null : (int) $sessionId;
$course = api_get_course_entity($courseId);
if (empty($course)) {
return false;
}
$session = null;
if (!empty($sessionId)) {
$session = api_get_session_entity($sessionId);
$courseExistsInSession = SessionManager::sessionHasCourse($sessionId, $course->getCode());
if (!$courseExistsInSession) {
return false;
}
}
// Delete old ones
$items = $em->getRepository('ChamiloSkillBundle:SkillRelCourse')->findBy(
['course' => $courseId, 'session' => $sessionId]
);
if (!empty($items)) {
/** @var SkillRelCourse $item */
foreach ($items as $item) {
if (!in_array($item->getSkill()->getId(), $skills)) {
$em->remove($item);
}
}
$em->flush();
}
// Add new one
if (!empty($skills)) {
foreach ($skills as $skillId) {
$item = new SkillRelCourse();
$item->setCourse($course);
$item->setSession($session);
/** @var SkillEntity $skill */
$skill = $em->getRepository('ChamiloCoreBundle:Skill')->find($skillId);
if ($skill) {
if (!$skill->hasCourseAndSession($item)) {
$skill->addToCourse($item);
$em->persist($skill);
}
}
}
$em->flush();
}
return true;
}
}

@ -924,6 +924,7 @@ class SocialManager extends UserManager
$messagesIcon = Display::return_icon('sn-message.png', get_lang('Messages'), null, ICON_SIZE_SMALL);
$sharedProfileIcon = Display::return_icon('sn-profile.png', get_lang('ViewMySharedProfile'));
$searchIcon = Display::return_icon('sn-search.png', get_lang('Search'), null, ICON_SIZE_SMALL);
$portfolioIcon = Display::return_icon('wiki_task.png', get_lang('Portfolio'));
$html = '';
$active = null;
@ -1002,6 +1003,15 @@ class SocialManager extends UserManager
$myFiles = '';
}
$links .= $myFiles;
if (api_get_configuration_value('allow_portfolio_tool')) {
$links .= '
<li class="portoflio-icon '.($show == 'portfolio' ? 'active' : '').'">
<a href="'.api_get_path(WEB_CODE_PATH).'portfolio/index.php">
'.$portfolioIcon.' '.get_lang('Portfolio').'
</a>
</li>
';
}
$links .= '</ul>';
$html .= Display::panelCollapse(
@ -1081,6 +1091,16 @@ class SocialManager extends UserManager
$myFiles = '';
}
$links .= $myFiles;
if (api_get_configuration_value('allow_portfolio_tool')) {
$links .= '
<li class="portoflio-icon '.($show == 'portfolio' ? 'active' : '').'">
<a href="'.api_get_path(WEB_CODE_PATH).'portfolio/index.php">
'.$portfolioIcon.' '.get_lang('Portfolio').'
</a>
</li>
';
}
}
// My friend profile.
@ -1106,6 +1126,16 @@ class SocialManager extends UserManager
]
);
$links .= '</li>';
if (api_get_configuration_value('allow_portfolio_tool')) {
$links .= '
<li class="portoflio-icon '.($show == 'portfolio' ? 'active' : '').'">
<a href="'.api_get_path(WEB_CODE_PATH).'portfolio/index.php?user='.$user_id.'">
'.$portfolioIcon.' '.get_lang('Portfolio').'
</a>
</li>
';
}
}
// Check if I already sent an invitation message
@ -1492,7 +1522,7 @@ class SocialManager extends UserManager
$messageId,
$fileComment = ''
) {
$tbl_message_attach = Database::get_main_table(TABLE_MESSAGE_ATTACHMENT);
$table = Database::get_main_table(TABLE_MESSAGE_ATTACHMENT);
// create directory
$social = '/social/';
@ -1533,7 +1563,7 @@ class SocialManager extends UserManager
'message_id' => $messageId,
'size' => $fileAttach['size'],
];
Database::insert($tbl_message_attach, $params);
Database::insert($table, $params);
$flag = true;
}

@ -235,8 +235,12 @@ class SortableTable extends HTML_Table
$params['spacesBeforeSeparator'] = '';
$params['spacesAfterSeparator'] = '';
$query_vars = array_keys($_GET);
$query_vars_needed = [$this->param_prefix.'column', $this->param_prefix.'direction', $this->param_prefix.'per_page'];
if (count($this->additional_parameters) > 0) {
$query_vars_needed = [
$this->param_prefix.'column',
$this->param_prefix.'direction',
$this->param_prefix.'per_page',
];
if (!empty($this->additional_parameters) && count($this->additional_parameters) > 0) {
$query_vars_needed = array_merge(
$query_vars_needed,
array_keys($this->additional_parameters)

@ -25,7 +25,7 @@ $group_disk_path = api_get_path(SYS_COURSE_PATH).$course_info['path'].'/document
$group_web_path = api_get_path(WEB_COURSE_PATH).$course_info['path'].'/document'.$groupdirpath.'/';
//get all group files and folders
$docs_and_folders = DocumentManager::get_all_document_data(
$docs_and_folders = DocumentManager::getAllDocumentData(
$course_info,
$groupdirpath,
$groupIid,
@ -35,12 +35,12 @@ $docs_and_folders = DocumentManager::get_all_document_data(
);
// get all group filenames
$array_to_search = is_array($docs_and_folders) ? $docs_and_folders : array();
$array_to_search = !empty($docs_and_folders) ? $docs_and_folders : array();
if (count($array_to_search) > 0) {
while (list($key) = each($array_to_search)) {
$all_files[] = basename($array_to_search[$key]['path']);
}
while (list($key) = each($array_to_search)) {
$all_files[] = basename($array_to_search[$key]['path']);
}
}
//get all svg and png group files

@ -16,7 +16,7 @@ $curdirpath='/images/gallery'; //path of library directory
$course_info = api_get_course_info();
//get all files and folders
$docs_and_folders = DocumentManager::get_all_document_data(
$docs_and_folders = DocumentManager::getAllDocumentData(
$course_info,
$curdirpath,
0,
@ -26,12 +26,12 @@ $docs_and_folders = DocumentManager::get_all_document_data(
);
//get all filenames
$array_to_search = is_array($docs_and_folders) ? $docs_and_folders : array();
$array_to_search = !empty($docs_and_folders) ? $docs_and_folders : array();
if (count($array_to_search) > 0) {
while (list($key) = each($array_to_search)) {
$all_files[] = basename($array_to_search[$key]['path']);
}
while (list($key) = each($array_to_search)) {
$all_files[] = basename($array_to_search[$key]['path']);
}
}
//get all svg and png files

@ -46,49 +46,106 @@ class TableSort
}
if ($type == SORT_REGULAR) {
$type = SORT_STRING;
if (self::is_image_column($data, $column)) {
$type = SORT_IMAGE;
} elseif (self::is_date_column($data, $column)) {
$type = SORT_DATE;
} elseif (self::is_numeric_column($data, $column)) {
$type = SORT_NUMERIC;
} else {
$type = SORT_STRING;
}
}
$function = self::getSortFunction($type, $direction, $column);
// Sort the content
usort($data, $function);
return $data;
}
/**
* @param $type
* @param $direction
* @param $column
*
* @return Closure
*/
public static function getSortFunction($type, $direction, $column)
{
$compareOperator = $direction == SORT_ASC ? '>' : '<=';
$compare_operator = $direction == SORT_ASC ? '>' : '<=';
switch ($type) {
case SORT_NUMERIC:
$compare_function = 'return strip_tags($a['.$column.']) '.$compare_operator.' strip_tags($b['.$column.']);';
$function = function ($a, $b) use ($column, $compareOperator) {
$result = strip_tags($a[$column]) <= strip_tags($b[$column]);
if ($compareOperator == '>') {
$result = strip_tags($a[$column]) > strip_tags($b[$column]);
}
return $result;
};
break;
case SORT_IMAGE:
$compare_function = 'return api_strnatcmp(api_strtolower(strip_tags($a['.$column.'], "<img>")), api_strtolower(strip_tags($b['.$column.'], "<img>"))) '.$compare_operator.' 0;';
$function = function ($a, $b) use ($column, $compareOperator) {
$result = api_strnatcmp(
api_strtolower(strip_tags($a[$column], "<img>")),
api_strtolower(strip_tags($b[$column], "<img>"))
) <= 0;
if ($compareOperator == '>') {
$result = api_strnatcmp(
api_strtolower(strip_tags($a[$column], "<img>")),
api_strtolower(strip_tags($b[$column], "<img>"))
) > 0;
}
return $result;
};
break;
case SORT_DATE:
$compare_function = 'return strtotime(strip_tags($a['.$column.'])) '.$compare_operator.' strtotime(strip_tags($b['.$column.']));';
$function = function ($a, $b) use ($column, $compareOperator) {
$result = strtotime(strip_tags($a[$column])) <= strtotime(strip_tags($b[$column]));
if ($compareOperator == '>') {
$result = strtotime(strip_tags($a[$column])) > strtotime(strip_tags($b[$column]));
}
return $result;
};
break;
case SORT_STRING:
default:
$compare_function = 'return api_strnatcmp(api_strtolower(strip_tags($a['.$column.'])), api_strtolower(strip_tags($b['.$column.']))) '.$compare_operator.' 0;';
$function = function ($a, $b) use ($column, $compareOperator) {
$result = api_strnatcmp(
api_strtolower(strip_tags($a[$column])),
api_strtolower(strip_tags($b[$column]))
) <= 0;
if ($compareOperator == '>') {
$result = api_strnatcmp(
api_strtolower(strip_tags($a[$column])),
api_strtolower(strip_tags($b[$column]))
) > 0;
}
return $result;
};
break;
}
// Sort the content
usort($data, create_function('$a, $b', $compare_function));
return $data;
return $function;
}
/**
* Sorts 2-dimensional table. It is possile changing the columns that will be shown and the way that the columns are to be sorted.
* Sorts 2-dimensional table. It is possible changing the columns that will be
* shown and the way that the columns are to be sorted.
*
* @param array $data the data to be sorted
* @param int $column The column on which the data should be sorted (default = 0)
* @param string $direction The direction to sort (SORT_ASC (default) orSORT_DESC)
* @param array $column_show The columns that we will show in the table i.e: $column_show = array('1','0','1') we will show the 1st and the 3th column.
* @param array $column_order Changes how the columns will be sorted ie. $column_order = array('0','3','2','3') The column [1] will be sorted like the column [3]
* @param constant $type How should data be sorted (SORT_REGULAR, SORT_NUMERIC, SORT_STRING, SORT_DATE, SORT_IMAGE)
* @param array $data the data to be sorted
* @param int $column The column on which the data should be sorted (default = 0)
* @param int $direction The direction to sort (SORT_ASC (default) or SORT_DESC)
* @param array $column_show The columns that we will show in the table
* i.e: $column_show = array('1','0','1') we will show the 1st and the 3th column.
* @param array $column_order Changes how the columns will be sorted
* ie. $column_order = array('0','3','2','3') The column [1] will be sorted like the column [3]
* @param int $type How should data be sorted (SORT_REGULAR, SORT_NUMERIC, SORT_STRING, SORT_DATE, SORT_IMAGE)
*
* @return array The sorted dataset
*
@ -184,25 +241,10 @@ class TableSort
$data = $new_data_order;
}
} else {
$compare_operator = $direction == SORT_ASC ? '>' : '<=';
switch ($type) {
case SORT_NUMERIC:
$compare_function = 'return strip_tags($a['.$column.']) '.$compare_operator.' strip_tags($b['.$column.']);';
break;
case SORT_IMAGE:
$compare_function = 'return api_strnatcmp(api_strtolower(strip_tags($a['.$column.'], "<img>")), api_strtolower(strip_tags($b['.$column.'], "<img>"))) '.$compare_operator.' 0;';
break;
case SORT_DATE:
$compare_function = 'return strtotime(strip_tags($a['.$column.'])) '.$compare_operator.' strtotime(strip_tags($b['.$column.']));';
break;
case SORT_STRING:
default:
$compare_function = 'return api_strnatcmp(api_strtolower(strip_tags($a['.$column.'])), api_strtolower(strip_tags($b['.$column.']))) '.$compare_operator.' 0;';
break;
}
$function = self::getSortFunction($type, $direction, $column);
// Sort the content
usort($data, create_function('$a, $b', $compare_function));
usort($data, $function);
}
if (is_array($column_show) && !empty($column_show)) {

@ -73,7 +73,12 @@ function api_set_encoding_html(&$string, $encoding)
}
} else {
$count = 1;
$string = str_ireplace('</head>', '<meta http-equiv="Content-Type" content="text/html; charset='.$encoding.'"/></head>', $string, $count);
$string = str_ireplace(
'</head>',
'<meta http-equiv="Content-Type" content="text/html; charset='.$encoding.'"/></head>',
$string,
$count
);
}
$string = api_convert_encoding($string, $encoding, $old_encoding);
}
@ -82,7 +87,8 @@ function api_set_encoding_html(&$string, $encoding)
* Returns the title of a html document.
*
* @param string $string the contents of the input document
* @param string $output_encoding The encoding of the retrieved title. If the value is not set, the system encoding is assumend.
* @param string $output_encoding The encoding of the retrieved title.
* If the value is not set, the system encoding is assumed.
* @param string $input_encoding The encoding of the input document. If the value is not set, it is detected.
*
* @return string the retrieved title, html-entities and extra-whitespace between the words are cleaned
@ -97,7 +103,17 @@ function api_get_title_html(&$string, $output_encoding = null, $input_encoding =
$input_encoding = api_detect_encoding_html($string);
}
return trim(@preg_replace('/\s+/', ' ', api_html_entity_decode(api_convert_encoding($matches[1], $output_encoding, $input_encoding), ENT_QUOTES, $output_encoding)));
return trim(
@preg_replace(
'/\s+/',
' ',
api_html_entity_decode(
api_convert_encoding($matches[1], $output_encoding, $input_encoding),
ENT_QUOTES,
$output_encoding
)
)
);
}
return '';
@ -114,7 +130,8 @@ define('_PCRE_XML_ENCODING', '/<\?xml.*encoding=[\'"](.*?)[\'"].*\?>/m');
* Detects encoding of xml-formatted text.
*
* @param string $string the input xml-formatted text
* @param string $default_encoding This is the default encoding to be returned if there is no way the xml-text's encoding to be detected. If it not spesified, the system encoding is assumed then.
* @param string $default_encoding This is the default encoding to be returned
* if there is no way the xml-text's encoding to be detected. If it not spesified, the system encoding is assumed then.
*
* @return string returns the detected encoding
*
@ -136,11 +153,13 @@ function api_detect_encoding_xml($string, $default_encoding = null)
}
/**
* Converts character encoding of a xml-formatted text. If inside the text the encoding is declared, it is modified accordingly.
* Converts character encoding of a xml-formatted text.
* If inside the text the encoding is declared, it is modified accordingly.
*
* @param string $string the text being converted
* @param string $to_encoding the encoding that text is being converted to
* @param string $from_encoding (optional) The encoding that text is being converted from. If it is omited, it is tried to be detected then.
* @param string $from_encoding (optional) The encoding that text is being converted from.
* If it is omited, it is tried to be detected then.
*
* @return string returns the converted xml-text
*/
@ -150,10 +169,12 @@ function api_convert_encoding_xml($string, $to_encoding, $from_encoding = null)
}
/**
* Converts character encoding of a xml-formatted text into UTF-8. If inside the text the encoding is declared, it is set to UTF-8.
* Converts character encoding of a xml-formatted text into UTF-8.
* If inside the text the encoding is declared, it is set to UTF-8.
*
* @param string $string the text being converted
* @param string $from_encoding (optional) The encoding that text is being converted from. If it is omited, it is tried to be detected then.
* @param string $from_encoding (optional) The encoding that text is being converted from.
* If it is omited, it is tried to be detected then.
*
* @return string returns the converted xml-text
*/
@ -163,10 +184,12 @@ function api_utf8_encode_xml($string, $from_encoding = null)
}
/**
* Converts character encoding of a xml-formatted text from UTF-8 into a specified encoding. If inside the text the encoding is declared, it is modified accordingly.
* Converts character encoding of a xml-formatted text from UTF-8 into a specified encoding.
* If inside the text the encoding is declared, it is modified accordingly.
*
* @param string $string the text being converted
* @param string $to_encoding (optional) The encoding that text is being converted to. If it is omited, the platform character set is assumed.
* @param string $to_encoding (optional) The encoding that text is being converted to.
* If it is omitted, the platform character set is assumed.
*
* @return string returns the converted xml-text
*/
@ -176,11 +199,13 @@ function api_utf8_decode_xml($string, $to_encoding = 'UTF-8')
}
/**
* Converts character encoding of a xml-formatted text. If inside the text the encoding is declared, it is modified accordingly.
* Converts character encoding of a xml-formatted text.
* If inside the text the encoding is declared, it is modified accordingly.
*
* @param string $string the text being converted
* @param string $to_encoding the encoding that text is being converted to
* @param string $from_encoding (optional) The encoding that text is being converted from. If the value is empty, it is tried to be detected then.
* @param string $from_encoding (optional) The encoding that text is being converted from.
* If the value is empty, it is tried to be detected then.
*
* @return string returns the converted xml-text
*/
@ -191,11 +216,16 @@ function _api_convert_encoding_xml(&$string, $to_encoding, $from_encoding)
}
$to_encoding = api_refine_encoding_id($to_encoding);
if (!preg_match('/<\?xml.*\?>/m', $string, $matches)) {
return api_convert_encoding('<?xml version="1.0" encoding="'.$to_encoding.'"?>'."\n".$string, $to_encoding, $from_encoding);
return api_convert_encoding(
'<?xml version="1.0" encoding="'.$to_encoding.'"?>'."\n".$string,
$to_encoding,
$from_encoding
);
}
if (!preg_match(_PCRE_XML_ENCODING, $string)) {
if (strpos($matches[0], 'standalone') !== false) {
// The encoding option should precede the standalone option, othewise DOMDocument fails to load the document.
// The encoding option should precede the standalone option,
// othewise DOMDocument fails to load the document.
$replace = str_replace('standalone', ' encoding="'.$to_encoding.'" standalone', $matches[0]);
} else {
$replace = str_replace('?>', ' encoding="'.$to_encoding.'"?>', $matches[0]);
@ -297,7 +327,8 @@ function api_camel_case_to_underscore($string)
* Works correctly with ASCII strings only, implementation for human-language strings is not necessary.
*
* @param string $string The input string (ASCII)
* @param bool $capitalise_first_char (optional) If true (default), the function capitalises the first char in the result string
* @param bool $capitalise_first_char (optional)
* If true (default), the function capitalises the first char in the result string
*
* @return string The converted result string
*/
@ -322,9 +353,11 @@ function _api_camelize($match)
* @author Brouckaert Olivier
*
* @param string $text the text to truncate
* @param int $length The approximate desired length. The length of the suffix below is to be added to have the total length of the result string.
* @param int $length The approximate desired length. The length of the suffix below is to be added to
* have the total length of the result string.
* @param string $suffix a suffix to be added as a replacement
* @param string $encoding (optional) The encoding to be used. If it is omitted, the platform character set will be used by default.
* @param string $encoding (optional) The encoding to be used. If it is omitted,
* the platform character set will be used by default.
* @param bool $middle if this parameter is true, truncation is done in the middle of the string
*
* @return string truncated string, decorated with the given suffix (replacement)
@ -339,7 +372,23 @@ function api_trunc_str($text, $length = 30, $suffix = '...', $middle = false, $e
return $text;
}
if ($middle) {
return rtrim(api_substr($text, 0, round($length / 2), $encoding)).$suffix.ltrim(api_substr($text, -round($length / 2), $text_length, $encoding));
return rtrim(
api_substr(
$text,
0,
round($length / 2),
$encoding
)
).
$suffix.
ltrim(
api_substr(
$text,
-round($length / 2),
$text_length,
$encoding
)
);
}
return rtrim(api_substr($text, 0, $length, $encoding)).$suffix;
@ -406,7 +455,8 @@ function _make_url_clickable_cb($matches)
$url = $matches[2];
if (')' == $matches[3] && strpos($url, '(')) {
// If the trailing character is a closing parethesis, and the URL has an opening parenthesis in it, add the closing parenthesis to the URL.
// If the trailing character is a closing parethesis, and the URL has an opening
// parenthesis in it, add the closing parenthesis to the URL.
// Then we can let the parenthesis balancer do its thing below.
$url .= $matches[3];
$suffix = '';
@ -442,7 +492,8 @@ function _make_url_clickable_cb($matches)
*
* @param string $url the URL to be cleaned
* @param array $protocols Optional. An array of acceptable protocols.
* Defaults to 'http', 'https', 'ftp', 'ftps', 'mailto', 'news', 'irc', 'gopher', 'nntp', 'feed', 'telnet', 'mms', 'rtsp', 'svn' if not set.
* Defaults to 'http', 'https', 'ftp', 'ftps', 'mailto', 'news', 'irc', 'gopher',
* 'nntp', 'feed', 'telnet', 'mms', 'rtsp', 'svn' if not set.
* @param string $_context Private. Use esc_url_raw() for database usage.
*
* @return string the cleaned $url after the 'clean_url' filter is applied
@ -506,7 +557,8 @@ function esc_url($url, $protocols = null, $_context = 'display')
*
* @since wordpress 2.8.1
*
* @param string|array $search The value being searched for, otherwise known as the needle. An array may be used to designate multiple needles.
* @param string|array $search The value being searched for, otherwise known as the needle.
* An array may be used to designate multiple needles.
* @param string $subject the string being searched and replaced on, otherwise known as the haystack
*
* @return string the string with the replaced svalues
@ -586,7 +638,8 @@ function _make_email_clickable_cb($matches)
function make_clickable($text)
{
$r = '';
$textarr = preg_split('/(<[^<>]+>)/', $text, -1, PREG_SPLIT_DELIM_CAPTURE); // split out HTML tags
// split out HTML tags
$textarr = preg_split('/(<[^<>]+>)/', $text, -1, PREG_SPLIT_DELIM_CAPTURE);
$nested_code_pre = 0; // Keep track of how many levels link is nested inside <pre> or <code>
foreach ($textarr as $piece) {
if (preg_match('|^<code[\s>]|i', $piece) || preg_match('|^<pre[\s>]|i', $piece)) {
@ -595,7 +648,10 @@ function make_clickable($text)
$nested_code_pre--;
}
if ($nested_code_pre || empty($piece) || ($piece[0] === '<' && !preg_match('|^<\s*[\w]{1,20}+://|', $piece))) {
if ($nested_code_pre ||
empty($piece) ||
($piece[0] === '<' && !preg_match('|^<\s*[\w]{1,20}+://|', $piece))
) {
$r .= $piece;
continue;
}
@ -603,7 +659,8 @@ function make_clickable($text)
// Long strings might contain expensive edge cases ...
if (10000 < strlen($piece)) {
// ... break it up
foreach (_split_str_by_whitespace($piece, 2100) as $chunk) { // 2100: Extra room for scheme and leading and trailing paretheses
foreach (_split_str_by_whitespace($piece, 2100) as $chunk) {
// 2100: Extra room for scheme and leading and trailing paretheses
if (2101 < strlen($chunk)) {
$r .= $chunk; // Too big, no whitespace: bail.
} else {
@ -629,8 +686,16 @@ function make_clickable($text)
// Tell PCRE to spend more time optimizing since, when used on a page load, it will probably be used several times.
$ret = preg_replace_callback($url_clickable, '_make_url_clickable_cb', $ret);
$ret = preg_replace_callback('#([\s>])((www|ftp)\.[\w\\x80-\\xff\#$%&~/.\-;:=,?@\[\]+]+)#is', '_make_web_ftp_clickable_cb', $ret);
$ret = preg_replace_callback('#([\s>])([.0-9a-z_+-]+)@(([0-9a-z-]+\.)+[0-9a-z]{2,})#i', '_make_email_clickable_cb', $ret);
$ret = preg_replace_callback(
'#([\s>])((www|ftp)\.[\w\\x80-\\xff\#$%&~/.\-;:=,?@\[\]+]+)#is',
'_make_web_ftp_clickable_cb',
$ret
);
$ret = preg_replace_callback(
'#([\s>])([.0-9a-z_+-]+)@(([0-9a-z-]+\.)+[0-9a-z]{2,})#i',
'_make_email_clickable_cb',
$ret
);
$ret = substr($ret, 1, -1); // Remove our whitespace padding.
$r .= $ret;
@ -651,7 +716,8 @@ function make_clickable($text)
*
* Joining the returned chunks with empty delimiters reconstructs the input string losslessly.
*
* Input string must have no null characters (or eventual transformations on output chunks must not care about null characters)
* Input string must have no null characters (or eventual transformations on output chunks
* must not care about null characters)
*
* <code>
* _split_str_by_whitespace( "1234 67890 1234 67890a cd 1234 890 123456789 1234567890a 45678 1 3 5 7 90 ", 10 ) ==
@ -728,19 +794,31 @@ function cut($text, $maxchar, $embed = false)
*
* @param mixed Number to convert
* @param int Decimal points 0=never, 1=if needed, 2=always
* @param string $decimalPoint
* @param string $thousandsSeparator
*
* @return mixed An integer or a float depends on the parameter
*/
function float_format($number, $flag = 1)
function float_format($number, $flag = 1, $decimalPoint = '.', $thousandsSeparator = ',')
{
if (is_numeric($number)) {
if (!$number) {
$result = ($flag == 2 ? '0.'.str_repeat('0', EXERCISE_NUMBER_OF_DECIMALS) : '0');
} else {
if (floor($number) == $number) {
$result = number_format($number, ($flag == 2 ? EXERCISE_NUMBER_OF_DECIMALS : 0));
$result = number_format(
$number,
($flag == 2 ? EXERCISE_NUMBER_OF_DECIMALS : 0),
$decimalPoint,
$thousandsSeparator
);
} else {
$result = number_format(round($number, 2), ($flag == 0 ? 0 : EXERCISE_NUMBER_OF_DECIMALS));
$result = number_format(
round($number, 2),
($flag == 0 ? 0 : EXERCISE_NUMBER_OF_DECIMALS),
$decimalPoint,
$thousandsSeparator
);
}
}
@ -795,7 +873,7 @@ function get_week_from_day($date)
/**
* This function splits the string into words and then joins them back together again one by one.
* Example: "Test example of a long string"
* substrwords(5) = Test ... *.
* substrwords(5) = Test ... *.
*
* @param string
* @param int the max number of character
@ -867,36 +945,6 @@ function format_file_size($file_size)
return $file_size;
}
function return_datetime_from_array($array)
{
$year = '0000';
$month = $day = $hours = $minutes = $seconds = '00';
if (isset($array['Y']) && (isset($array['F']) || isset($array['M'])) && isset($array['d']) && isset($array['H']) && isset($array['i'])) {
$year = $array['Y'];
$month = isset($array['F']) ? $array['F'] : $array['M'];
if (intval($month) < 10) {
$month = '0'.$month;
}
$day = $array['d'];
if (intval($day) < 10) {
$day = '0'.$day;
}
$hours = $array['H'];
if (intval($hours) < 10) {
$hours = '0'.$hours;
}
$minutes = $array['i'];
if (intval($minutes) < 10) {
$minutes = '0'.$minutes;
}
}
if (checkdate($month, $day, $year)) {
$datetime = $year.'-'.$month.'-'.$day.' '.$hours.':'.$minutes.':'.$seconds;
}
return $datetime;
}
/**
* Converts an string CLEANYO[admin][amann,acostea]
* into an array:.

@ -500,6 +500,7 @@ class Thematic
global $thematic_id;
$table = Database::get_course_table(TABLE_THEMATIC_ADVANCE);
$course_id = api_get_course_int_id();
$thematic_id = (int) $thematic_id;
$sql = "SELECT COUNT(id) AS total_number_of_items
FROM $table
@ -534,6 +535,7 @@ class Thematic
}
$data = [];
$course_id = api_get_course_int_id();
$thematic_id = (int) $thematic_id;
if (api_is_allowed_to_edit(null, true)) {
$sql = "SELECT id AS col0, start_date AS col1, duration AS col2, content AS col3
FROM $table
@ -559,8 +561,10 @@ class Thematic
$thematic_advance[1] = api_get_local_time($thematic_advance[1]);
$thematic_advance[1] = api_format_date($thematic_advance[1], DATE_TIME_FORMAT_LONG);
$actions = '';
$actions .= '<a href="index.php?'.api_get_cidreq().'&action=thematic_advance_edit&thematic_id='.$thematic_id.'&thematic_advance_id='.$thematic_advance[0].'">'.Display::return_icon('edit.png', get_lang('Edit'), '', 22).'</a>';
$actions .= '<a onclick="javascript:if(!confirm(\''.get_lang('AreYouSureToDelete').'\')) return false;" href="index.php?'.api_get_cidreq().'&action=thematic_advance_delete&thematic_id='.$thematic_id.'&thematic_advance_id='.$thematic_advance[0].'">'.Display::return_icon('delete.png', get_lang('Delete'), '', 22).'</a></center>';
$actions .= '<a href="index.php?'.api_get_cidreq().'&action=thematic_advance_edit&thematic_id='.$thematic_id.'&thematic_advance_id='.$thematic_advance[0].'">'.
Display::return_icon('edit.png', get_lang('Edit'), '', 22).'</a>';
$actions .= '<a onclick="javascript:if(!confirm(\''.get_lang('AreYouSureToDelete').'\')) return false;" href="index.php?'.api_get_cidreq().'&action=thematic_advance_delete&thematic_id='.$thematic_id.'&thematic_advance_id='.$thematic_advance[0].'">'.
Display::return_icon('delete.png', get_lang('Delete'), '', 22).'</a></center>';
$data[] = [$i, $thematic_advance[1], $thematic_advance[2], $thematic_advance[3], $actions];
$i++;
}
@ -585,7 +589,7 @@ class Thematic
// set current course
$table = Database::get_course_table(TABLE_THEMATIC_ADVANCE);
$thematic_id = intval($thematic_id);
$thematic_id = (int) $thematic_id;
$data = [];
$sql = "SELECT * FROM $table
WHERE c_id = $course_id AND thematic_id = $thematic_id ";
@ -631,8 +635,9 @@ class Thematic
}
}
// DATE_TIME_FORMAT_LONG
$thematic_advance_item = '<div><strong>'.api_convert_and_format_date($thematic_advance['start_date'], DATE_TIME_FORMAT_LONG).$session_star.'</strong></div>';
// $thematic_advance_item .= '<div>'.get_lang('DurationInHours').' : '.$thematic_advance['duration'].'</div>';
$thematic_advance_item = '<div><strong>'.
api_convert_and_format_date($thematic_advance['start_date'], DATE_TIME_FORMAT_LONG).
$session_star.'</strong></div>';
$thematic_advance_item .= '<div>'.$thematic_advance['duration'].' '.get_lang('HourShort').'</div>';
$thematic_advance_item .= '<div>'.Security::remove_XSS($thematic_advance['content'], STUDENT).'</div>';
$return_array[$thematic_id][$thematic_advance['id']] = $thematic_advance_item;
@ -838,8 +843,7 @@ class Thematic
/**
* delete thematic advance.
*
* @param int Thematic advance id
* @param int $id
* @param int $id Thematic advance id
*
* @return int Affected rows
*/

@ -73,12 +73,15 @@ class Tracking
$courseInfo['real_id'],
$sessionId
);
$avg_student_score += self::get_avg_student_score(
$average = self::get_avg_student_score(
$user_data['user_id'],
$courseInfo['code'],
[],
$sessionId
);
if (is_numeric($average)) {
$avg_student_score += $average;
}
$avg_student_progress += self::get_avg_student_progress(
$user_data['user_id'],
$courseInfo['code'],
@ -1006,12 +1009,9 @@ class Tracking
$my_exo_exe_id = $row_attempts['exe_exo_id'];
$mktime_start_date = api_strtotime($row_attempts['start_date'], 'UTC');
$mktime_exe_date = api_strtotime($row_attempts['exe_date'], 'UTC');
$time_attemp = ' - ';
if ($mktime_start_date && $mktime_exe_date) {
$mytime = ((int) $mktime_exe_date - (int) $mktime_start_date);
$time_attemp = learnpathItem::getScormTimeFromParameter('js', $mytime);
$time_attemp = str_replace('NaN', '00'.$h.'00\'00"', $time_attemp);
} else {
$time_attemp = ' - ';
$time_attemp = api_format_time($row_attempts['exe_duration'], 'js');
}
if (!$is_allowed_to_edit && $result_disabled_ext_all) {
$view_score = Display::return_icon(
@ -2684,7 +2684,6 @@ class Tracking
// Check the real number of LPs corresponding to the filter in the
// database (and if no list was given, get them all)
if (empty($session_id)) {
$sql = "SELECT DISTINCT(id), use_max_score
FROM $lp_table
@ -3007,7 +3006,6 @@ class Tracking
if ($debug) {
echo '<h3>Final return</h3>';
}
if ($lp_with_quiz != 0) {
if (!$return_array) {
$score_of_scorm_calculate = round(($global_result / $lp_with_quiz), 2);
@ -6558,15 +6556,16 @@ class Tracking
/**
* Get the HTML code for show a block with the achieved user skill on course/session.
*
* @param int $userId
* @param int $courseId optional
* @param int $sessionId optional
* @param int $userId
* @param int $courseId
* @param int $sessionId
* @param bool $forceView forces the view of the skills, not checking for deeper access
*
* @return string
*/
public static function displayUserSkills($userId, $courseId = 0, $sessionId = 0)
public static function displayUserSkills($userId, $courseId = 0, $sessionId = 0, $forceView = false)
{
if (Skill::isAllowed($userId, false) === false) {
if (Skill::isAllowed($userId, false) === false && $forceView == false) {
return '';
}
$skillManager = new Skill();

@ -208,8 +208,8 @@ class UserGroup extends Model
/**
* Gets a list of course ids by user group.
*
* @param int $id user group id
* @param array $loadCourseData
* @param int $id user group id
* @param bool $loadCourseData
*
* @return array
*/
@ -483,16 +483,17 @@ class UserGroup extends Model
/**
* Gets a list of user ids by user group.
*
* @param int $id user group id
* @param int $id user group id
* @param array $roles
*
* @return array with a list of user ids
*/
public function get_users_by_usergroup($id = null, $relationList = [])
public function get_users_by_usergroup($id = null, $roles = [])
{
$relationCondition = '';
if (!empty($relationList)) {
if (!empty($roles)) {
$relationConditionArray = [];
foreach ($relationList as $relation) {
foreach ($roles as $relation) {
$relation = (int) $relation;
if (empty($relation)) {
$relationConditionArray[] = " (relation_type = 0 OR relation_type IS NULL OR relation_type = '') ";
@ -501,7 +502,7 @@ class UserGroup extends Model
}
}
$relationCondition = " AND ( ";
$relationCondition .= implode("AND", $relationConditionArray);
$relationCondition .= implode('OR', $relationConditionArray);
$relationCondition .= " ) ";
}
@ -968,7 +969,7 @@ class UserGroup extends Model
foreach ($result as $group) {
$group['sessions'] = count($this->get_sessions_by_usergroup($group['id']));
$group['courses'] = count($this->get_courses_by_usergroup($group['id']));
$roles = [];
switch ($group['group_type']) {
case 0:
$group['group_type'] = Display::label(get_lang('Class'), 'info');
@ -2027,8 +2028,15 @@ class UserGroup extends Model
} else {
$num = intval($num);
}
$where_relation_condition = " WHERE g.group_type = ".self::SOCIAL_CLASS." AND
gu.relation_type IN ('".GROUP_USER_PERMISSION_ADMIN."' , '".GROUP_USER_PERMISSION_READER."', '".GROUP_USER_PERMISSION_HRM."') ";
$where = " WHERE
g.group_type = ".self::SOCIAL_CLASS." AND
gu.relation_type IN
('".GROUP_USER_PERMISSION_ADMIN."' ,
'".GROUP_USER_PERMISSION_READER."',
'".GROUP_USER_PERMISSION_MODERATOR."',
'".GROUP_USER_PERMISSION_HRM."')
";
$sql = "SELECT DISTINCT
count(user_id) as count,
g.picture,
@ -2038,7 +2046,7 @@ class UserGroup extends Model
FROM $tbl_group g
INNER JOIN $table_group_rel_user gu
ON gu.usergroup_id = g.id
$where_relation_condition
$where
GROUP BY g.id
ORDER BY created_at DESC
LIMIT $num ";

@ -305,6 +305,7 @@ class UserManager
$currentDate = api_get_utc_datetime();
$now = new DateTime();
if (empty($expirationDate) || $expirationDate == '0000-00-00 00:00:00') {
// Default expiration date
// if there is a default duration of a valid account then
@ -326,7 +327,7 @@ class UserManager
/** @var User $user */
$user = $userManager->createUser();
error_log('langua5');
$user
->setLastname($lastName)
->setFirstname($firstName)
@ -476,18 +477,70 @@ class UserManager
'password' => $original_password,
];
api_mail_html(
$recipient_name,
$email,
$emailSubject,
$emailBody,
$sender_name,
$email_admin,
null,
null,
null,
$additionalParameters
);
$twoEmail = api_get_configuration_value('send_two_inscription_confirmation_mail');
if ($twoEmail === true) {
$layoutContent = $tplContent->get_template('mail/new_user_first_email_confirmation.tpl');
$emailBody = $tplContent->fetch($layoutContent);
api_mail_html(
$recipient_name,
$email,
$emailSubject,
$emailBody,
$sender_name,
$email_admin,
null,
null,
null,
$additionalParameters
);
$layoutContent = $tplContent->get_template('mail/new_user_second_email_confirmation.tpl');
$emailBody = $tplContent->fetch($layoutContent);
api_mail_html(
$recipient_name,
$email,
$emailSubject,
$emailBody,
$sender_name,
$email_admin,
null,
null,
null,
$additionalParameters
);
} else {
$sendToInbox = api_get_configuration_value('send_inscription_msg_to_inbox');
if ($sendToInbox) {
$adminList = UserManager::get_all_administrators();
$senderId = 1;
if (!empty($adminList)) {
$adminInfo = current($adminList);
$senderId = $adminInfo['user_id'];
}
MessageManager::send_message_simple(
$userId,
$emailSubject,
$emailBody,
$senderId
);
} else {
api_mail_html(
$recipient_name,
$email,
$emailSubject,
$emailBody,
$sender_name,
$email_admin,
null,
null,
null,
$additionalParameters
);
}
}
$notification = api_get_configuration_value('send_notification_when_user_added');
if (!empty($notification) && isset($notification['admins']) && is_array($notification['admins'])) {

@ -311,7 +311,7 @@ class Rest extends WebService
}
$courseInfo = api_get_course_info_by_id($this->course->getId());
$documents = DocumentManager::get_all_document_data(
$documents = DocumentManager::getAllDocumentData(
$courseInfo,
$path,
0,
@ -322,7 +322,7 @@ class Rest extends WebService
);
$results = [];
if (is_array($documents)) {
if (!empty($documents)) {
$webPath = api_get_path(WEB_CODE_PATH).'document/document.php?';
/** @var array $document */
@ -1031,135 +1031,127 @@ class Rest extends WebService
}
/**
* @param array $course_param
* @param array $courseParam
*
* @return array
*/
public function addCourse($course_param)
public function addCourse(array $courseParam)
{
$table_course = Database::get_main_table(TABLE_MAIN_COURSE);
$extra_list = [];
$title = isset($course_param['title']) ? $course_param['title'] : '';
$category_code = isset($course_param['category_code']) ? $course_param['category_code'] : '';
$wanted_code = isset($course_param['wanted_code']) ? intval($course_param['wanted_code']) : 0;
$tutor_name = isset($course_param['tutor_name']) ? $course_param['tutor_name'] : '';
$admin_id = isset($course_param['admin_id']) ? $course_param['admin_id'] : null;
$language = isset($course_param['language']) ? $course_param['language'] : null;
$original_course_id = isset($course_param['original_course_id']) ? $course_param['original_course_id'] : null;
$diskQuota = isset($course_param['disk_quota']) ? $course_param['disk_quota'] : '100';
$visibility = isset($course_param['visibility']) ? (int) $course_param['visibility'] : null;
if (isset($course_param['visibility'])) {
if ($course_param['visibility'] &&
$course_param['visibility'] >= 0 &&
$course_param['visibility'] <= 3
$tableCourse = Database::get_main_table(TABLE_MAIN_COURSE);
$extraList = [];
$results = [];
$title = isset($courseParam['title']) ? $courseParam['title'] : '';
$categoryCode = isset($courseParam['category_code']) ? $courseParam['category_code'] : '';
$wantedCode = isset($courseParam['wanted_code']) ? intval($courseParam['wanted_code']) : 0;
$tutorName = isset($courseParam['tutor_name']) ? $courseParam['tutor_name'] : '';
$courseLanguage = isset($courseParam['language']) ? $courseParam['language'] : null;
$originalCourseIdName = isset($courseParam['original_course_id_name'])
? $courseParam['original_course_id_name']
: null;
$originalCourseIdValue = isset($courseParam['original_course_id_value'])
? $courseParam['original_course_id_value']
: null;
$diskQuota = isset($courseParam['disk_quota']) ? $courseParam['disk_quota'] : '100';
$visibility = isset($courseParam['visibility']) ? (int) $courseParam['visibility'] : null;
if (isset($courseParam['visibility'])) {
if ($courseParam['visibility'] &&
$courseParam['visibility'] >= 0 &&
$courseParam['visibility'] <= 3
) {
$visibility = (int) $course_param['visibility'];
$visibility = (int) $courseParam['visibility'];
}
}
// Check whether exits $x_course_code into user_field_values table.
$courseInfo = CourseManager::getCourseInfoFromOriginalId(
'id',
$course_param['original_course_id_name']
$originalCourseIdValue,
$originalCourseIdName
);
if (!empty($courseInfo)) {
if ($courseInfo['visibility'] != 0) {
$sql = "UPDATE $table_course SET
course_language = '".Database::escape_string($course_language)."',
$sql = "UPDATE $tableCourse SET
course_language = '".Database::escape_string($courseLanguage)."',
title = '".Database::escape_string($title)."',
category_code = '".Database::escape_string($category_code)."',
tutor_name = '".Database::escape_string($tutor_name)."',
visual_code = '".Database::escape_string($wanted_code)."'";
category_code = '".Database::escape_string($categoryCode)."',
tutor_name = '".Database::escape_string($tutorName)."',
visual_code = '".Database::escape_string($wantedCode)."'";
if ($visibility !== null) {
$sql .= ", visibility = $visibility ";
}
$sql .= " WHERE id = ".$courseInfo['real_id'];
Database::query($sql);
if (is_array($extra_list) && count($extra_list) > 0) {
foreach ($extra_list as $extra) {
$extra_field_name = $extra['field_name'];
$extra_field_value = $extra['field_value'];
if (is_array($extraList) && count($extraList) > 0) {
foreach ($extraList as $extra) {
$extraFieldName = $extra['field_name'];
$extraFieldValue = $extra['field_value'];
// Save the external system's id into course_field_value table.
CourseManager::update_course_extra_field_value(
$courseInfo['code'],
$extra_field_name,
$extra_field_value
$extraFieldName,
$extraFieldValue
);
}
}
$results[] = $courseInfo['code'];
} else {
$results[] = 0;
}
}
if (!empty($course_param['course_language'])) {
$course_language = $course_param['course_language'];
}
$params = [];
$params['title'] = $title;
$params['wanted_code'] = $wanted_code;
$params['category_code'] = $category_code;
$params['course_category'] = $category_code;
$params['tutor_name'] = $tutor_name;
$params['course_language'] = $course_language;
$params['wanted_code'] = $wantedCode;
$params['category_code'] = $categoryCode;
$params['course_category'] = $categoryCode;
$params['tutor_name'] = $tutorName;
$params['course_language'] = $courseLanguage;
$params['user_id'] = $this->user->getId();
$params['visibility'] = $visibility;
$params['disk_quota'] = $diskQuota;
$params['subscribe'] = empty($courseParam['subscribe']) ? 0 : 1;
$params['unsubscribe'] = empty($courseParam['unsubscribe']) ? 0 : 1;
if (isset($subscribe) && $subscribe != '') { // Valid values: 0, 1
$params['subscribe'] = $subscribe;
}
if (isset($unsubscribe) && $subscribe != '') { // Valid values: 0, 1
$params['unsubscribe'] = $unsubscribe;
}
$course_info = CourseManager::create_course($params, $params['user_id']);
$courseInfo = CourseManager::create_course($params, $params['user_id']);
if (!empty($course_info)) {
$course_code = $course_info['code'];
if (!empty($courseInfo)) {
$courseCode = $courseInfo['code'];
// Save new field label into course_field table
CourseManager::create_course_extra_field(
$original_course_id_name,
$originalCourseIdName,
1,
$original_course_id_name,
$originalCourseIdName,
''
);
// Save the external system's id into user_field_value table.
CourseManager::update_course_extra_field_value(
$course_code,
$original_course_id_name,
$original_course_id_value
$courseCode,
$originalCourseIdName,
$originalCourseIdValue
);
if (is_array($extra_list) && count($extra_list) > 0) {
foreach ($extra_list as $extra) {
$extra_field_name = $extra['field_name'];
$extra_field_value = $extra['field_value'];
if (is_array($extraList) && count($extraList) > 0) {
foreach ($extraList as $extra) {
$extraFieldName = $extra['field_name'];
$extraFieldValue = $extra['field_value'];
// Save new fieldlabel into course_field table.
CourseManager::create_course_extra_field(
$extra_field_name,
$extraFieldName,
1,
$extra_field_name,
$extraFieldName,
''
);
// Save the external system's id into course_field_value table.
CourseManager::update_course_extra_field_value(
$course_code,
$extra_field_name,
$extra_field_value
$courseCode,
$extraFieldName,
$extraFieldValue
);
}
}
$results[] = $course_code;
} else {
$results[] = 0;
$results[] = $courseCode;
}
return $results;

File diff suppressed because it is too large Load Diff

@ -49,29 +49,6 @@ if ($allowMessage) {
Display::return_icon('outbox.png', get_lang('Outbox')).'</a>';
}
$info_delete_outbox = [];
$info_delete_outbox = isset($_GET['form_delete_outbox']) ? explode(',', $_GET['form_delete_outbox']) : '';
$count_delete_outbox = count($info_delete_outbox) - 1;
if (isset($info_delete_outbox[0]) && trim($info_delete_outbox[0]) == 'delete') {
for ($i = 1; $i <= $count_delete_outbox; $i++) {
MessageManager::delete_message_by_user_sender(api_get_user_id(), $info_delete_outbox[$i]);
}
$message_box = get_lang('SelectedMessagesDeleted').
'&nbsp
<br><a href="../social/index.php?#remote-tab-3">'.
get_lang('BackToOutbox').
'</a>';
Display::addFlash(
Display::return_message(
api_xml_http_response_encode($message_box),
'normal',
false
)
);
exit;
}
$action = null;
if (isset($_REQUEST['action'])) {
$action = $_REQUEST['action'];

@ -1604,13 +1604,23 @@ if (empty($details)) {
<?php
} //end details
echo Tracking::displayUserSkills(
$user_info['user_id'],
$courseInfo ? $courseInfo['real_id'] : 0,
$sessionId,
api_get_configuration_value('allow_teacher_access_student_skills')
);
$allowAll = api_get_configuration_value('allow_teacher_access_student_skills');
if ($allowAll) {
// Show all skills
echo Tracking::displayUserSkills(
$user_info['user_id'],
0,
0,
true
);
} else {
// Default behaviour - Show all skills depending the course and session id
echo Tracking::displayUserSkills(
$user_info['user_id'],
$courseInfo ? $courseInfo['real_id'] : 0,
$sessionId
);
}
if ($allowMessages === true) {
// Messages
echo Display::page_subheader2(get_lang('Messages'));

@ -82,7 +82,7 @@ if ($session) {
$usersInfo[$user->getId()][$course->getId().'_score'] = Tracking::get_avg_student_score(
$user->getId(),
$course->getCode(),
null,
[],
$session->getId(),
false,
false,
@ -91,7 +91,7 @@ if ($session) {
$usersInfo[$user->getId()][$course->getId().'_progress'] = Tracking::get_avg_student_progress(
$user->getId(),
$course->getCode(),
null,
[],
$session->getId()
);

@ -1,6 +1,6 @@
<?php
/* For licensing terms, see /license.txt */
/**
* Display part of the SCORM sub-process for upload. This script MUST BE included by upload/index.php
* as it prepares most of the variables needed here.
@ -39,7 +39,7 @@ function get_zip_files_in_garbage()
/**
* Just display the form needed to upload a SCORM and give its settings.
*/
$nameTools = get_lang("FileUpload");
$nameTools = get_lang('FileUpload');
$interbreadcrumb[] = [
"url" => api_get_path(WEB_CODE_PATH)."lp/lp_controller.php?action=list?".api_get_cidreq(),
"name" => get_lang("ToolLearnpath"),

Loading…
Cancel
Save