From 6401e71af68b616f5aadc21b87302d97aa115b41 Mon Sep 17 00:00:00 2001 From: christianbeeznst Date: Mon, 14 Oct 2024 01:25:20 -0500 Subject: [PATCH 01/11] Session: Add batch session export to CSV - refs BT#22096 --- public/main/inc/lib/TrackingCourseLog.php | 1277 ++++++++++++++++ public/main/inc/lib/exercise.lib.php | 40 - public/main/inc/lib/extra_field_value.lib.php | 34 +- public/main/inc/lib/sessionmanager.lib.php | 265 +++- public/main/inc/lib/tracking.lib.php | 1321 ++--------------- public/main/inc/lib/usergroup.lib.php | 2 + public/main/session/session_list.php | 85 +- public/main/tracking/courseLog.php | 4 +- public/main/tracking/course_log_resources.php | 2 +- 9 files changed, 1741 insertions(+), 1289 deletions(-) create mode 100644 public/main/inc/lib/TrackingCourseLog.php diff --git a/public/main/inc/lib/TrackingCourseLog.php b/public/main/inc/lib/TrackingCourseLog.php new file mode 100644 index 00000000000..fbe1a8e6e30 --- /dev/null +++ b/public/main/inc/lib/TrackingCourseLog.php @@ -0,0 +1,1277 @@ +total_number_of_items; + } + + /** + * Retrieves item resources data with pagination and sorting options. + */ + public static function getItemResourcesData($from, $numberOfItems, $column, $direction): array + { + $sessionId = api_get_session_id(); + $courseId = api_get_course_int_id(); + + $tableItemProperty = Database::get_course_table(TABLE_ITEM_PROPERTY); + $tableUser = Database::get_main_table(TABLE_MAIN_USER); + $tableSession = Database::get_main_table(TABLE_MAIN_SESSION); + $column = (int) $column; + $direction = !in_array(strtolower(trim($direction)), ['asc', 'desc']) ? 'asc' : $direction; + + $sql = "SELECT + tool as col0, + lastedit_type as col1, + ref as ref, + user.username as col3, + insert_date as col6, + visibility as col7, + user.user_id as user_id + FROM $tableItemProperty track_resource, $tableUser user + WHERE + track_resource.c_id = $courseId AND + track_resource.insert_user_id = user.user_id AND + session_id ".(empty($sessionId) ? ' IS NULL ' : " = $sessionId "); + + if (isset($_GET['keyword'])) { + $keyword = Database::escape_string(trim($_GET['keyword'])); + $sql .= " AND ( + user.username LIKE '%".$keyword."%' OR + lastedit_type LIKE '%".$keyword."%' OR + tool LIKE '%".$keyword."%' + ) "; + } + + $sql .= " AND tool IN ( + 'document', + 'learnpath', + 'quiz', + 'glossary', + 'link', + 'course_description', + 'announcement', + 'thematic', + 'thematic_advance', + 'thematic_plan' + )"; + + if (0 == $column) { + $column = '0'; + } + if ('' != $column && '' != $direction) { + if (2 != $column && 4 != $column) { + $sql .= " ORDER BY col$column $direction"; + } + } else { + $sql .= " ORDER BY col6 DESC "; + } + + $from = intval($from); + if ($from) { + $numberOfItems = intval($numberOfItems); + $sql .= " LIMIT $from, $numberOfItems "; + } + + $res = Database::query($sql); + $resources = []; + $thematicTools = ['thematic', 'thematic_advance', 'thematic_plan']; + while ($row = Database::fetch_array($res)) { + $ref = $row['ref']; + $tableName = self::getToolNameTable($row['col0']); + $tableTool = Database::get_course_table($tableName['table_name']); + + $id = $tableName['id_tool']; + $recorset = false; + + if (in_array($row['col0'], ['thematic_plan', 'thematic_advance'])) { + $tblThematic = Database::get_course_table(TABLE_THEMATIC); + $sql = "SELECT thematic_id FROM $tableTool + WHERE c_id = $courseId AND id = $ref"; + $rsThematic = Database::query($sql); + if (Database::num_rows($rsThematic)) { + $rowThematic = Database::fetch_array($rsThematic); + $thematicId = $rowThematic['thematic_id']; + + $sql = "SELECT session.id, session.name, user.username + FROM $tblThematic t, $tableSession session, $tableUser user + WHERE + t.c_id = $courseId AND + t.session_id = session.id AND + session.id_coach = user.user_id AND + t.id = $thematicId"; + $recorset = Database::query($sql); + } + } else { + $sql = "SELECT session.id, session.name, user.username + FROM $tableTool tool, $tableSession session, $tableUser user + WHERE + tool.c_id = $courseId AND + tool.session_id = session.id AND + session.id_coach = user.user_id AND + tool.$id = $ref"; + $recorset = Database::query($sql); + } + + if (!empty($recorset)) { + $obj = Database::fetch_object($recorset); + + $nameSession = ''; + $coachName = ''; + if (!empty($obj)) { + $nameSession = $obj->name; + $coachName = $obj->username; + } + + $urlTool = api_get_path(WEB_CODE_PATH).$tableName['link_tool']; + + if ($row['col6'] != 2) { + if (in_array($row['col0'], $thematicTools)) { + $expThematicTool = explode('_', $row['col0']); + $thematicTooltitle = ''; + if (is_array($expThematicTool)) { + foreach ($expThematicTool as $exp) { + $thematicTooltitle .= api_ucfirst($exp); + } + } else { + $thematicTooltitle = api_ucfirst($row['col0']); + } + + $row[0] = ''.get_lang( + $thematicTooltitle + ).''; + } else { + $row[0] = ''.get_lang( + 'Tool'.api_ucfirst($row['col0']) + ).''; + } + } else { + $row[0] = api_ucfirst($row['col0']); + } + $row[1] = get_lang($row[1]); + $row[6] = api_convert_and_format_date($row['col6'], null, date_default_timezone_get()); + $row[5] = ''; + //@todo Improve this code please + switch ($tableName['table_name']) { + case 'document': + $sql = "SELECT tool.title as title FROM $tableTool tool + WHERE c_id = $courseId AND id = $ref"; + $rsDocument = Database::query($sql); + $objDocument = Database::fetch_object($rsDocument); + if ($objDocument) { + $row[5] = $objDocument->title; + } + break; + case 'quiz': + case 'course_description': + case 'announcement': + $sql = "SELECT title FROM $tableTool + WHERE c_id = $courseId AND id = $ref"; + $rsDocument = Database::query($sql); + $objDocument = Database::fetch_object($rsDocument); + if ($objDocument) { + $row[5] = $objDocument->title; + } + break; + case 'glossary': + $sql = "SELECT name FROM $tableTool + WHERE c_id = $courseId AND glossary_id = $ref"; + $rsDocument = Database::query($sql); + $objDocument = Database::fetch_object($rsDocument); + if ($objDocument) { + $row[5] = $objDocument->name; + } + break; + case 'lp': + $sql = "SELECT name + FROM $tableTool WHERE c_id = $courseId AND id = $ref"; + $rsDocument = Database::query($sql); + $objDocument = Database::fetch_object($rsDocument); + $row[5] = $objDocument->name; + break; + case 'thematic_plan': + case 'thematic': + $rs = Database::query("SELECT title FROM $tableTool WHERE c_id = $courseId AND id = $ref"); + if (Database::num_rows($rs) > 0) { + $obj = Database::fetch_object($rs); + if ($obj) { + $row[5] = $obj->title; + } + } + break; + case 'thematic_advance': + $rs = Database::query("SELECT content FROM $tableTool WHERE c_id = $courseId AND id = $ref"); + if (Database::num_rows($rs) > 0) { + $obj = Database::fetch_object($rs); + if ($obj) { + $row[5] = $obj->content; + } + } + break; + case 'thematic_plan': + $rs = Database::query("SELECT title FROM $tableTool WHERE c_id = $courseId AND id = $ref"); + if (Database::num_rows($rs) > 0) { + $obj = Database::fetch_object($rs); + if ($obj) { + $row[5] = $obj->title; + } + } + break; + default: + break; + } + + $row2 = $nameSession; + if (!empty($coachName)) { + $row2 .= '
'.get_lang('Coach').': '.$coachName; + } + $row[2] = $row2; + if (!empty($row['col3'])) { + $userInfo = api_get_user_info($row['user_id']); + $row['col3'] = Display::url( + $row['col3'], + $userInfo['profile_url'] + ); + $row[3] = $row['col3']; + + $ip = Tracking::get_ip_from_user_event( + $row['user_id'], + $row['col6'], + true + ); + if (empty($ip)) { + $ip = get_lang('Unknown'); + } + $row[4] = $ip; + } + + $resources[] = $row; + } + } + + return $resources; + } + + /** + * Retrieves the name and associated table for a given tool. + */ + public static function getToolNameTable(string $tool): array + { + $linkTool = ''; + $idTool = ''; + + switch ($tool) { + case 'document': + $tableName = TABLE_DOCUMENT; + $linkTool = 'document/document.php'; + $idTool = 'id'; + break; + case 'learnpath': + $tableName = TABLE_LP_MAIN; + $linkTool = 'lp/lp_controller.php'; + $idTool = 'id'; + break; + case 'quiz': + $tableName = TABLE_QUIZ_TEST; + $linkTool = 'exercise/exercise.php'; + $idTool = 'iid'; + break; + case 'glossary': + $tableName = TABLE_GLOSSARY; + $linkTool = 'glossary/index.php'; + $idTool = 'glossary_id'; + break; + case 'link': + $tableName = TABLE_LINK; + $linkTool = 'link/link.php'; + $idTool = 'id'; + break; + case 'course_description': + $tableName = TABLE_COURSE_DESCRIPTION; + $linkTool = 'course_description/'; + $idTool = 'id'; + break; + case 'announcement': + $tableName = TABLE_ANNOUNCEMENT; + $linkTool = 'announcements/announcements.php'; + $idTool = 'id'; + break; + case 'thematic': + $tableName = TABLE_THEMATIC; + $linkTool = 'course_progress/index.php'; + $idTool = 'id'; + break; + case 'thematic_advance': + $tableName = TABLE_THEMATIC_ADVANCE; + $linkTool = 'course_progress/index.php'; + $idTool = 'id'; + break; + case 'thematic_plan': + $tableName = TABLE_THEMATIC_PLAN; + $linkTool = 'course_progress/index.php'; + $idTool = 'id'; + break; + default: + $tableName = $tool; + break; + } + + return [ + 'table_name' => $tableName, + 'link_tool' => $linkTool, + 'id_tool' => $idTool, + ]; + } + + /** + * Displays additional profile fields, excluding specific fields if provided. + */ + public static function displayAdditionalProfileFields(array $exclude = [], $formAction = null): string + { + $formAction = $formAction ?: 'courseLog.php'; + + // getting all the extra profile fields that are defined by the platform administrator + $extraFields = UserManager::get_extra_fields(0, 50); + + // creating the form + $return = '
'; + // the select field with the additional user profile fields, this is where we select the field of which we want to see + // the information the users have entered or selected. + $return .= '
'; + $return .= ''; + $return .= '
'; + + // the form elements for the $_GET parameters (because the form is passed through GET + foreach ($_GET as $key => $value) { + if ($key != 'additional_profile_field') { + $return .= ''; + } + } + // the submit button + $return .= '
'; + $return .= ''; + $return .= '
'; + $return .= '
'; + + return $extraFieldsToShow > 0 ? $return : ''; + } + + /** + * This function gets all the information of a certrain ($field_id) + * additional profile field for a specific list of users is more efficent + * than get_addtional_profile_information_of_field() function + * It gets the information of all the users so that it can be displayed + * in the sortable table or in the csv or xls export. + * + * @param int $fieldId field id + * @param array $users list of user ids + * + * @author Julio Montoya + * + * @since Nov 2009 + * + * @version 1.8.6.2 + */ + public static function getAdditionalProfileInformationOfFieldByUser($fieldId, $users): array + { + // Database table definition + $tableUser = Database::get_main_table(TABLE_MAIN_USER); + $tableUserFieldValues = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES); + $extraField = Database::get_main_table(TABLE_EXTRA_FIELD); + $resultExtraField = UserManager::get_extra_field_information($fieldId); + $return = []; + if (!empty($users)) { + if ($resultExtraField['field_type'] == UserManager::USER_FIELD_TYPE_TAG) { + foreach ($users as $user_id) { + $userResult = UserManager::get_user_tags($user_id, $fieldId); + $tagList = []; + foreach ($userResult as $item) { + $tagList[] = $item['tag']; + } + $return[$user_id][] = implode(', ', $tagList); + } + } else { + $newUserArray = []; + foreach ($users as $user_id) { + $newUserArray[] = "'".$user_id."'"; + } + $users = implode(',', $newUserArray); + $extraFieldType = EntityExtraField::USER_FIELD_TYPE; + // Selecting only the necessary information NOT ALL the user list + $sql = "SELECT user.user_id, v.value + FROM $tableUser user + INNER JOIN $tableUserFieldValues v + ON (user.user_id = v.item_id) + INNER JOIN $extraField f + ON (f.id = v.field_id) + WHERE + f.extra_field_type = $extraFieldType AND + v.field_id=".intval($fieldId)." AND + user.user_id IN ($users)"; + + $result = Database::query($sql); + while ($row = Database::fetch_array($result)) { + // get option value for field type double select by id + if (!empty($row['value'])) { + if ($resultExtraField['field_type'] == + ExtraField::FIELD_TYPE_DOUBLE_SELECT + ) { + $idDoubleSelect = explode(';', $row['value']); + if (is_array($idDoubleSelect)) { + $value1 = $resultExtraField['options'][$idDoubleSelect[0]]['option_value']; + $value2 = $resultExtraField['options'][$idDoubleSelect[1]]['option_value']; + $row['value'] = ($value1.';'.$value2); + } + } + + if ($resultExtraField['field_type'] == ExtraField::FIELD_TYPE_SELECT_WITH_TEXT_FIELD) { + $parsedValue = explode('::', $row['value']); + + if ($parsedValue) { + $value1 = $resultExtraField['options'][$parsedValue[0]]['display_text']; + $value2 = $parsedValue[1]; + + $row['value'] = "$value1: $value2"; + } + } + + if ($resultExtraField['field_type'] == ExtraField::FIELD_TYPE_TRIPLE_SELECT) { + [$level1, $level2, $level3] = explode(';', $row['value']); + + $row['value'] = $resultExtraField['options'][$level1]['display_text'].' / '; + $row['value'] .= $resultExtraField['options'][$level2]['display_text'].' / '; + $row['value'] .= $resultExtraField['options'][$level3]['display_text']; + } + } + // get other value from extra field + $return[$row['user_id']][] = $row['value']; + } + } + } + + return $return; + } + + /** + * Get number of users for sortable with pagination. + */ + public static function getNumberOfUsers(array $conditions): int + { + $conditions['get_count'] = true; + + return self::getUserData(0, 0, 0, '', $conditions); + } + + /** + * Get data for users list in sortable with pagination. + */ + public static function getUserData( + $from, + $numberOfItems, + $column, + $direction, + array $conditions = [], + bool $exerciseToCheckConfig = true, + bool $displaySessionInfo = false, + ?string $courseCode = null, + ?int $sessionId = null, + bool $exportCsv = false, + array $userIds = [] + ) { + $includeInvitedUsers = $conditions['include_invited_users'] ?? false; + $getCount = $conditions['get_count'] ?? false; + + $csvContent = []; + $tblUser = Database::get_main_table(TABLE_MAIN_USER); + $tblUrlRelUser = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER); + $accessUrlId = api_get_current_access_url_id(); + + if (!empty($userIds) && is_array($userIds)) { + $userIds = array_map('intval', $userIds); + $conditionUser = " WHERE user.id IN (".implode(',', $userIds).") "; + } else { + $conditionUser = " WHERE user.id = " . (int) $userIds; + } + + if (!empty($_GET['user_keyword'])) { + $keyword = trim(Database::escape_string($_GET['user_keyword'])); + $conditionUser .= " AND ( + user.firstname LIKE '%".$keyword."%' OR + user.lastname LIKE '%".$keyword."%' OR + user.username LIKE '%".$keyword."%' OR + user.email LIKE '%".$keyword."%' + ) "; + } + + $urlTable = ''; + $urlCondition = ''; + if (api_is_multiple_url_enabled()) { + $urlTable = " INNER JOIN $tblUrlRelUser as url_users ON (user.id = url_users.user_id)"; + $urlCondition = " AND access_url_id = '$accessUrlId'"; + } + + $invitedUsersCondition = ''; + if (!$includeInvitedUsers) { + $invitedUsersCondition = " AND user.status != ".INVITEE; + } + + $select = ' + SELECT user.id as user_id, + user.official_code as col0, + user.lastname as col1, + user.firstname as col2, + user.username as col3, + user.email as col4'; + + if ($getCount) { + $select = ' SELECT COUNT(distinct(user.id)) as count '; + } + + $sqlInjectJoins = ''; + $where = 'AND 1 = 1 '; + $sqlInjectWhere = ''; + if (!empty($conditions)) { + if (isset($conditions['inject_joins'])) { + $sqlInjectJoins = $conditions['inject_joins']; + } + if (isset($conditions['where'])) { + $where = $conditions['where']; + } + if (isset($conditions['inject_where'])) { + $sqlInjectWhere = $conditions['inject_where']; + } + $injectExtraFields = !empty($conditions['inject_extra_fields']) ? $conditions['inject_extra_fields'] : 1; + $injectExtraFields = rtrim($injectExtraFields, ', '); + if (false === $getCount) { + $select .= " , $injectExtraFields"; + } + } + + $sql = "$select + FROM $tblUser as user + $urlTable + $sqlInjectJoins + $conditionUser + $urlCondition + $invitedUsersCondition + $where + $sqlInjectWhere + "; + + if (!in_array($direction, ['ASC', 'DESC'])) { + $direction = 'ASC'; + } + + $column = $column <= 2 ? (int) $column : 0; + $from = (int) $from; + $numberOfItems = (int) $numberOfItems; + + if ($getCount) { + $res = Database::query($sql); + $row = Database::fetch_array($res); + + return $row['count']; + } + + $sortByFirstName = api_sort_by_first_name(); + + if ($sortByFirstName) { + if ($column == 1) { + $column = 2; + } elseif ($column == 2) { + $column = 1; + } + } + + $sql .= " ORDER BY col$column $direction "; + $sql .= " LIMIT $from, $numberOfItems"; + + $res = Database::query($sql); + $users = []; + + $courseInfo = api_get_course_info($courseCode); + $courseId = $courseInfo['real_id']; + + $totalSurveys = 0; + $totalExercises = ExerciseLib::get_all_exercises( + $courseInfo, + $sessionId, + false, + null, + false, + 3 + ); + + if (empty($sessionId)) { + $surveyUserList = []; + $surveyList = SurveyManager::get_surveys($courseCode, $sessionId); + if ($surveyList) { + $totalSurveys = count($surveyList); + foreach ($surveyList as $survey) { + $userList = SurveyManager::get_people_who_filled_survey( + $survey['survey_id'], + false, + $courseId + ); + + foreach ($userList as $user_id) { + isset($surveyUserList[$user_id]) ? $surveyUserList[$user_id]++ : $surveyUserList[$user_id] = 1; + } + } + } + } + + $urlBase = api_get_path(WEB_CODE_PATH).'mySpace/myStudents.php?details=true&cidReq='.$courseCode. + '&course='.$courseCode.'&origin=tracking_course&id_session='.$sessionId; + + Session::write('user_id_list', []); + $userIdList = []; + + if ($exerciseToCheckConfig) { + $addExerciseOption = api_get_setting('exercise.add_exercise_best_attempt_in_report', true); + $exerciseResultsToCheck = []; + if (!empty($addExerciseOption) && isset($addExerciseOption['courses']) && + isset($addExerciseOption['courses'][$courseCode]) + ) { + foreach ($addExerciseOption['courses'][$courseCode] as $exerciseId) { + $exercise = new Exercise(); + $exercise->read($exerciseId); + if ($exercise->iid) { + $exerciseResultsToCheck[] = $exercise; + } + } + } + } + + $lpShowMaxProgress = 'true' === api_get_setting('lp.lp_show_max_progress_instead_of_average'); + if ('true' === api_get_setting('lp.lp_show_max_progress_or_average_enable_course_level_redefinition')) { + $lpShowProgressCourseSetting = api_get_course_setting('lp_show_max_or_average_progress', $courseInfo, true); + if (in_array($lpShowProgressCourseSetting, ['max', 'average'])) { + $lpShowMaxProgress = ('max' === $lpShowProgressCourseSetting); + } + } + + while ($user = Database::fetch_array($res, 'ASSOC')) { + $userIdList[] = $user['user_id']; + $user['official_code'] = $user['col0']; + $user['username'] = $user['col3']; + $user['time'] = api_time_to_hms( + Tracking::get_time_spent_on_the_course( + $user['user_id'], + $courseId, + $sessionId + ) + ); + + $avgStudentScore = Tracking::get_avg_student_score( + $user['user_id'], + api_get_course_entity($courseId), + [], + api_get_session_entity($sessionId) + ); + + $averageBestScore = Tracking::get_avg_student_score( + $user['user_id'], + api_get_course_entity($courseId), + [], + api_get_session_entity($sessionId), + false, + false, + true + ); + + $avgStudentProgress = Tracking::get_avg_student_progress( + $user['user_id'], + api_get_course_entity($courseId), + [], + api_get_session_entity($sessionId) + ); + + if (empty($avgStudentProgress)) { + $avgStudentProgress = 0; + } + $user['average_progress'] = $avgStudentProgress.'%'; + + $totalUserExercise = Tracking::get_exercise_student_progress( + $totalExercises, + $user['user_id'], + $courseId, + $sessionId + ); + + $user['exercise_progress'] = $totalUserExercise; + + $totalUserExercise = Tracking::get_exercise_student_average_best_attempt( + $totalExercises, + $user['user_id'], + $courseId, + $sessionId + ); + + $user['exercise_average_best_attempt'] = $totalUserExercise; + + if (is_numeric($avgStudentScore)) { + $user['student_score'] = $avgStudentScore.'%'; + } else { + $user['student_score'] = $avgStudentScore; + } + + if (is_numeric($averageBestScore)) { + $user['student_score_best'] = $averageBestScore.'%'; + } else { + $user['student_score_best'] = $averageBestScore; + } + + $exerciseResults = []; + if (!empty($exerciseResultsToCheck)) { + foreach ($exerciseResultsToCheck as $exercise) { + $bestExerciseResult = Event::get_best_attempt_exercise_results_per_user( + $user['user_id'], + $exercise->iid, + $courseId, + $sessionId, + false + ); + + $best = null; + if ($bestExerciseResult) { + $best = $bestExerciseResult['exe_result'] / $bestExerciseResult['exe_weighting']; + $best = round($best, 2) * 100; + $best .= '%'; + } + $exerciseResults['exercise_'.$exercise->iid] = $best; + } + } + + $user['first_connection'] = Tracking::get_first_connection_date_on_the_course( + $user['user_id'], + $courseId, + $sessionId, + !$exportCsv + ); + + $user['last_connection'] = Tracking::get_last_connection_date_on_the_course( + $user['user_id'], + $courseInfo, + $sessionId, + !$exportCsv + ); + + + $user['count_assignments'] = Tracking::countStudentPublications( + $courseId, + $sessionId + ); + + $user['count_messages'] = Tracking::countStudentMessages( + $courseId, + $sessionId + ); + + + $user['lp_finalization_date'] = Tracking::getCourseLpFinalizationDate( + $user['user_id'], + $courseId, + $sessionId, + !$exportCsv + ); + + $user['quiz_finalization_date'] = Tracking::getCourseQuizLastFinalizationDate( + $user['user_id'], + $courseId, + $sessionId, + !$exportCsv + ); + + if ($exportCsv) { + if (!empty($user['first_connection'])) { + $user['first_connection'] = api_get_local_time($user['first_connection']); + } else { + $user['first_connection'] = '-'; + } + if (!empty($user['last_connection'])) { + $user['last_connection'] = api_get_local_time($user['last_connection']); + } else { + $user['last_connection'] = '-'; + } + if (!empty($user['lp_finalization_date'])) { + $user['lp_finalization_date'] = api_get_local_time($user['lp_finalization_date']); + } else { + $user['lp_finalization_date'] = '-'; + } + if (!empty($user['quiz_finalization_date'])) { + $user['quiz_finalization_date'] = api_get_local_time($user['quiz_finalization_date']); + } else { + $user['quiz_finalization_date'] = '-'; + } + } + + if (empty($sessionId)) { + $user['survey'] = ($surveyUserList[$user['user_id']] ?? 0).' / '.$totalSurveys; + } + + $url = $urlBase.'&student='.$user['user_id']; + + $user['link'] = ' + '.Display::return_icon('2rightarrow.png', get_lang('Details')).' + '; + + // store columns in array $users + $userRow = []; + if ($displaySessionInfo && !empty($sessionId)) { + $sessionInfo = api_get_session_info($sessionId); + $userRow['session_name'] = $sessionInfo['name']; + $userRow['session_startdate'] = $sessionInfo['access_start_date']; + $userRow['session_enddate'] = $sessionInfo['access_end_date']; + $userRow['course_name'] = $courseInfo['name']; + } + $userRow['official_code'] = $user['official_code']; + if ($sortByFirstName) { + $userRow['firstname'] = $user['col2']; + $userRow['lastname'] = $user['col1']; + } else { + $userRow['lastname'] = $user['col1']; + $userRow['firstname'] = $user['col2']; + } + $userRow['username'] = $user['username']; + $userRow['time'] = $user['time']; + $userRow['average_progress'] = $user['average_progress']; + $userRow['exercise_progress'] = $user['exercise_progress']; + $userRow['exercise_average_best_attempt'] = $user['exercise_average_best_attempt']; + $userRow['student_score'] = $user['student_score']; + $userRow['student_score_best'] = $user['student_score_best']; + if (!empty($exerciseResults)) { + foreach ($exerciseResults as $exerciseId => $bestResult) { + $userRow[$exerciseId] = $bestResult; + } + } + + $userRow['count_assignments'] = $user['count_assignments']; + $userRow['count_messages'] = $user['count_messages']; + + $userGroupManager = new UserGroupModel(); + if ($exportCsv) { + $userRow['classes'] = implode( + ',', + $userGroupManager->getNameListByUser($user['user_id'], UserGroupModel::NORMAL_CLASS) + ); + } else { + $userRow['classes'] = $userGroupManager->getLabelsFromNameList( + $user['user_id'], + UserGroupModel::NORMAL_CLASS + ); + } + + if (empty($sessionId)) { + $userRow['survey'] = $user['survey']; + } else { + $userSession = SessionManager::getUserSession($user['user_id'], $sessionId); + $userRow['registered_at'] = ''; + if ($userSession) { + $userRow['registered_at'] = api_get_local_time($userSession['registered_at']); + } + } + + $userRow['first_connection'] = $user['first_connection']; + $userRow['last_connection'] = $user['last_connection']; + + $userRow['lp_finalization_date'] = $user['lp_finalization_date']; + $userRow['quiz_finalization_date'] = $user['quiz_finalization_date']; + + // we need to display an additional profile field + if (isset($_GET['additional_profile_field'])) { + $data = Session::read('additional_user_profile_info'); + + $extraFieldInfo = Session::read('extra_field_info'); + foreach ($_GET['additional_profile_field'] as $fieldId) { + if (isset($data[$fieldId]) && isset($data[$fieldId][$user['user_id']])) { + if (is_array($data[$fieldId][$user['user_id']])) { + $userRow[$extraFieldInfo[$fieldId]['variable']] = implode( + ', ', + $data[$fieldId][$user['user_id']] + ); + } else { + $userRow[$extraFieldInfo[$fieldId]['variable']] = $data[$fieldId][$user['user_id']]; + } + } else { + $userRow[$extraFieldInfo[$fieldId]['variable']] = ''; + } + } + } + + $data = Session::read('default_additional_user_profile_info'); + $defaultExtraFieldInfo = Session::read('default_extra_field_info'); + if (isset($defaultExtraFieldInfo) && isset($data)) { + foreach ($data as $key => $val) { + if (isset($val[$user['user_id']])) { + if (is_array($val[$user['user_id']])) { + $userRow[$defaultExtraFieldInfo[$key]['variable']] = implode( + ', ', + $val[$user['user_id']] + ); + } else { + $userRow[$defaultExtraFieldInfo[$key]['variable']] = $val[$user['user_id']]; + } + } else { + $userRow[$defaultExtraFieldInfo[$key]['variable']] = ''; + } + } + } + + if (api_get_setting('show_email_addresses') === 'true') { + $userRow['email'] = $user['col4']; + } + + $userRow['link'] = $user['link']; + + if ($exportCsv) { + unset($userRow['link']); + $csvContent[] = $userRow; + } + $users[] = array_values($userRow); + } + + if ($exportCsv) { + Session::write('csv_content', $csvContent); + } + + Session::erase('additional_user_profile_info'); + Session::erase('extra_field_info'); + Session::erase('default_additional_user_profile_info'); + Session::erase('default_extra_field_info'); + Session::write('user_id_list', $userIdList); + + return $users; + } + + /** + * Get data for users list in sortable with pagination. + */ + public static function getTotalTimeReport( + $from, + $numberOfItems, + $column, + $direction, + bool $includeInvitedUsers = false + ): array { + global $user_ids, $course_code, $export_csv, $session_id; + + $course_code = Database::escape_string($course_code); + $tblUser = Database::get_main_table(TABLE_MAIN_USER); + $tblUrlRelUser = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER); + $accessUrlId = api_get_current_access_url_id(); + + // get all users data from a course for sortable with limit + if (is_array($user_ids)) { + $user_ids = array_map('intval', $user_ids); + $conditionUser = " WHERE user.user_id IN (".implode(',', $user_ids).") "; + } else { + $user_ids = intval($user_ids); + $conditionUser = " WHERE user.user_id = $user_ids "; + } + + $urlTable = null; + $urlCondition = null; + if (api_is_multiple_url_enabled()) { + $urlTable = ", ".$tblUrlRelUser." as url_users"; + $urlCondition = " AND user.user_id = url_users.user_id AND access_url_id='$accessUrlId'"; + } + + $invitedUsersCondition = ''; + if (!$includeInvitedUsers) { + $invitedUsersCondition = " AND user.status != ".INVITEE; + } + + $sql = "SELECT user.user_id as user_id, + user.official_code as col0, + user.lastname as col1, + user.firstname as col2, + user.username as col3 + FROM $tblUser as user $urlTable + $conditionUser $urlCondition $invitedUsersCondition"; + + if (!in_array($direction, ['ASC', 'DESC'])) { + $direction = 'ASC'; + } + + $column = (int) $column; + $from = (int) $from; + $numberOfItems = (int) $numberOfItems; + + $sql .= " ORDER BY col$column $direction "; + $sql .= " LIMIT $from,$numberOfItems"; + + $res = Database::query($sql); + $users = []; + + $sortByFirstName = api_sort_by_first_name(); + $courseInfo = api_get_course_info($course_code); + $courseId = $courseInfo['real_id']; + + while ($user = Database::fetch_array($res, 'ASSOC')) { + $user['official_code'] = $user['col0']; + $user['lastname'] = $user['col1']; + $user['firstname'] = $user['col2']; + $user['username'] = $user['col3']; + + $totalCourseTime = Tracking::get_time_spent_on_the_course( + $user['user_id'], + $courseId, + $session_id + ); + + $user['time'] = api_time_to_hms($totalCourseTime); + $totalLpTime = Tracking::get_time_spent_in_lp( + $user['user_id'], + api_get_course_entity($courseId), + [], + $session_id + ); + + $warning = ''; + if ($totalLpTime > $totalCourseTime) { + $warning = ' '.Display::label(get_lang('TimeDifference'), 'danger'); + } + + $user['total_lp_time'] = api_time_to_hms($totalLpTime).$warning; + + $user['first_connection'] = Tracking::get_first_connection_date_on_the_course( + $user['user_id'], + $courseId, + $session_id + ); + $user['last_connection'] = Tracking::get_last_connection_date_on_the_course( + $user['user_id'], + $courseInfo, + $session_id, + $export_csv === false + ); + + $user['link'] = '
+ + '.Display::return_icon('2rightarrow.png', get_lang('Details')).' + +
'; + + // store columns in array $users + $userRow = []; + $userRow['official_code'] = $user['official_code']; //0 + if ($sortByFirstName) { + $userRow['firstname'] = $user['firstname']; + $userRow['lastname'] = $user['lastname']; + } else { + $userRow['lastname'] = $user['lastname']; + $userRow['firstname'] = $user['firstname']; + } + $userRow['username'] = $user['username']; + $userRow['time'] = $user['time']; + $userRow['total_lp_time'] = $user['total_lp_time']; + $userRow['first_connection'] = $user['first_connection']; + $userRow['last_connection'] = $user['last_connection']; + + $userRow['link'] = $user['link']; + $users[] = array_values($userRow); + } + + return $users; + } + + /** + * Determines the remaining actions for a session and returns a string with the results. + */ + public static function actionsLeft($current, $sessionId = 0): string + { + $usersLink = Display::url( + Display::return_icon('user.png', get_lang('StudentsTracking'), [], ICON_SIZE_MEDIUM), + 'courseLog.php?'.api_get_cidreq(true, false) + ); + + $groupsLink = Display::url( + Display::return_icon('group.png', get_lang('GroupReporting'), [], ICON_SIZE_MEDIUM), + 'course_log_groups.php?'.api_get_cidreq() + ); + + $resourcesLink = Display::url( + Display::return_icon('tools.png', get_lang('ResourcesTracking'), [], ICON_SIZE_MEDIUM), + 'course_log_resources.php?'.api_get_cidreq(true, false) + ); + + $courseLink = Display::url( + Display::return_icon('course.png', get_lang('CourseTracking'), [], ICON_SIZE_MEDIUM), + 'course_log_tools.php?'.api_get_cidreq(true, false) + ); + + $examLink = Display::url( + Display::return_icon('quiz.png', get_lang('ExamTracking'), [], ICON_SIZE_MEDIUM), + api_get_path(WEB_CODE_PATH).'tracking/exams.php?'.api_get_cidreq() + ); + + $eventsLink = Display::url( + Display::return_icon('security.png', get_lang('EventsReport'), [], ICON_SIZE_MEDIUM), + api_get_path(WEB_CODE_PATH).'tracking/course_log_events.php?'.api_get_cidreq() + ); + + $lpLink = Display::url( + Display::return_icon('scorms.png', get_lang('CourseLearningPathsGenericStats'), [], ICON_SIZE_MEDIUM), + api_get_path(WEB_CODE_PATH).'tracking/lp_report.php?'.api_get_cidreq() + ); + + $attendanceLink = ''; + if (!empty($sessionId)) { + $attendanceLink = Display::url( + Display::return_icon('attendance_list.png', get_lang('Logins'), '', ICON_SIZE_MEDIUM), + api_get_path(WEB_CODE_PATH).'attendance/index.php?'.api_get_cidreq().'&action=calendar_logins' + ); + } + + switch ($current) { + case 'users': + $usersLink = Display::url( + Display::return_icon( + 'user_na.png', + get_lang('StudentsTracking'), + [], + ICON_SIZE_MEDIUM + ), + '#' + ); + break; + case 'groups': + $groupsLink = Display::url( + Display::return_icon('group_na.png', get_lang('GroupReporting'), [], ICON_SIZE_MEDIUM), + '#' + ); + break; + case 'courses': + $courseLink = Display::url( + Display::return_icon('course_na.png', get_lang('CourseTracking'), [], ICON_SIZE_MEDIUM), + '#' + ); + break; + case 'resources': + $resourcesLink = Display::url( + Display::return_icon( + 'tools_na.png', + get_lang('ResourcesTracking'), + [], + ICON_SIZE_MEDIUM + ), + '#' + ); + break; + case 'exams': + $examLink = Display::url( + Display::return_icon('quiz_na.png', get_lang('ExamTracking'), [], ICON_SIZE_MEDIUM), + '#' + ); + break; + case 'logs': + $eventsLink = Display::url( + Display::return_icon('security_na.png', get_lang('EventsReport'), [], ICON_SIZE_MEDIUM), + '#' + ); + break; + case 'attendance': + if (!empty($sessionId)) { + $attendanceLink = Display::url( + Display::return_icon('attendance_list.png', get_lang('Logins'), '', ICON_SIZE_MEDIUM), + '#' + ); + } + break; + case 'lp': + $lpLink = Display::url( + Display::return_icon( + 'scorms_na.png', + get_lang('CourseLearningPathsGenericStats'), + [], + ICON_SIZE_MEDIUM + ), + '#' + ); + break; + } + + $items = [ + $usersLink, + $groupsLink, + $courseLink, + $resourcesLink, + $examLink, + $eventsLink, + $lpLink, + $attendanceLink, + ]; + + return implode('', $items).' '; + } +} diff --git a/public/main/inc/lib/exercise.lib.php b/public/main/inc/lib/exercise.lib.php index 656ab7c73a8..b539ec391d3 100644 --- a/public/main/inc/lib/exercise.lib.php +++ b/public/main/inc/lib/exercise.lib.php @@ -3003,46 +3003,6 @@ public static function get_all_exercises( $repo = Container::getQuizRepository(); return $repo->findAllByCourse($course, $session, (string) $search, $active); - - // Show courses by active status - /*if (true == $search_all_sessions) { - $conditions = [ - 'where' => [ - $active_sql.' c_id = ? '.$needle_where.$timeConditions => [ - $course_id, - $needle, - ], - ], - 'order' => 'title', - ]; - } else { - if (empty($session_id)) { - $conditions = [ - 'where' => [ - $active_sql.' (session_id = 0 OR session_id IS NULL) AND c_id = ? '.$needle_where.$timeConditions => [ - $course_id, - $needle, - ], - ], - 'order' => 'title', - ]; - } else { - $conditions = [ - 'where' => [ - $active_sql.' (session_id = 0 OR session_id IS NULL OR session_id = ? ) AND c_id = ? '.$needle_where.$timeConditions => [ - $session_id, - $course_id, - $needle, - ], - ], - 'order' => 'title', - ]; - } - } - - $table = Database::get_course_table(TABLE_QUIZ_TEST); - - return Database::select('*', $table, $conditions);*/ } /** diff --git a/public/main/inc/lib/extra_field_value.lib.php b/public/main/inc/lib/extra_field_value.lib.php index 8999fbfb81a..633f20e7cfc 100644 --- a/public/main/inc/lib/extra_field_value.lib.php +++ b/public/main/inc/lib/extra_field_value.lib.php @@ -220,6 +220,10 @@ public function saveFieldValues( $tags = []; foreach ($tagValues as $tagValue) { + if (is_array($tagValue)) { + $tagValue = reset($tagValue); + } + if (empty($tagValue)) { continue; } @@ -338,16 +342,26 @@ public function saveFieldValues( break; case ExtraField::FIELD_TYPE_DATE: - $d = DateTime::createFromFormat('Y-m-d', $value); - $valid = $d && $d->format('Y-m-d') === $value; - if ($valid) { - $newParams = [ - 'item_id' => $params['item_id'], - 'field_id' => $extraFieldInfo['id'], - 'field_value' => $value, - 'comment' => $comment, - ]; - $this->save($newParams, $showQuery); + if (is_array($value)) { + if (empty($value)) { + break; + } + $value = reset($value); + } + + if (is_string($value) && !empty($value)) { + $d = DateTime::createFromFormat('Y-m-d', $value); + $valid = $d && $d->format('Y-m-d') === $value; + + if ($valid) { + $newParams = [ + 'item_id' => $params['item_id'], + 'field_id' => $extraFieldInfo['id'], + 'field_value' => $value, + 'comment' => $comment, + ]; + $this->save($newParams, $showQuery); + } } break; case ExtraField::FIELD_TYPE_DATETIME: diff --git a/public/main/inc/lib/sessionmanager.lib.php b/public/main/inc/lib/sessionmanager.lib.php index eff09bb834c..9359acf2b65 100644 --- a/public/main/inc/lib/sessionmanager.lib.php +++ b/public/main/inc/lib/sessionmanager.lib.php @@ -8533,7 +8533,7 @@ public static function getGridColumns( [ 'name' => 'title', 'index' => 's.title', - 'width' => '160', + 'width' => '300', 'align' => 'left', 'search' => 'true', 'searchoptions' => ['sopt' => $operators], @@ -8549,6 +8549,7 @@ public static function getGridColumns( 'name' => 'display_start_date', 'index' => 'display_start_date', 'align' => 'left', + 'width' => '200', 'search' => 'true', 'searchoptions' => [ 'dataInit' => 'date_pick_today', @@ -8559,6 +8560,7 @@ public static function getGridColumns( 'name' => 'display_end_date', 'index' => 'display_end_date', 'align' => 'left', + 'width' => '200', 'search' => 'true', 'searchoptions' => [ 'dataInit' => 'date_pick_one_month', @@ -8579,6 +8581,7 @@ public static function getGridColumns( 'name' => 'users', 'index' => 'users', 'align' => 'left', + 'width' => '100', 'search' => 'false', ]; @@ -8603,6 +8606,7 @@ public static function getGridColumns( 'name' => 'status', 'index' => 'status', 'align' => 'left', + 'width' => '120', 'search' => 'true', 'stype' => 'select', // for the bottom bar @@ -8633,7 +8637,7 @@ public static function getGridColumns( [ 'name' => 'title', 'index' => 's.title', - 'width' => '160', + 'width' => '300', 'align' => 'left', 'search' => 'true', 'searchoptions' => ['sopt' => $operators], @@ -8649,6 +8653,7 @@ public static function getGridColumns( 'name' => 'display_start_date', 'index' => 'display_start_date', 'align' => 'left', + 'width' => '200', 'search' => 'true', 'searchoptions' => [ 'dataInit' => 'date_pick_today', @@ -8659,6 +8664,7 @@ public static function getGridColumns( 'name' => 'display_end_date', 'index' => 'display_end_date', 'align' => 'left', + 'width' => '200', 'search' => 'true', 'searchoptions' => [ 'dataInit' => 'date_pick_one_month', @@ -8679,6 +8685,7 @@ public static function getGridColumns( 'name' => 'users', 'index' => 'users', 'align' => 'left', + 'width' => '100', 'search' => 'false', ]; @@ -8722,7 +8729,7 @@ public static function getGridColumns( [ 'name' => 'title', 'index' => 's.title', - 'width' => '200', + 'width' => '300', 'align' => 'left', 'search' => 'true', 'searchoptions' => ['sopt' => $operators], @@ -8731,12 +8738,14 @@ public static function getGridColumns( 'name' => 'display_start_date', 'index' => 'display_start_date', 'align' => 'left', + 'width' => '200', 'search' => 'true', 'searchoptions' => ['dataInit' => 'date_pick_today', 'sopt' => $date_operators], ], [ 'name' => 'display_end_date', 'index' => 'display_end_date', + 'width' => '200', 'align' => 'left', 'search' => 'true', 'searchoptions' => ['dataInit' => 'date_pick_one_month', 'sopt' => $date_operators], @@ -8801,7 +8810,7 @@ public static function getGridColumns( [ 'name' => 'title', 'index' => 's.title', - 'width' => '160', + 'width' => '300', 'align' => 'left', 'search' => 'true', 'searchoptions' => ['sopt' => $operators], @@ -8817,6 +8826,7 @@ public static function getGridColumns( 'name' => 'display_start_date', 'index' => 'display_start_date', 'align' => 'left', + 'width' => '200', 'search' => 'true', 'searchoptions' => [ 'dataInit' => 'date_pick_today', @@ -8827,6 +8837,7 @@ public static function getGridColumns( 'name' => 'display_end_date', 'index' => 'display_end_date', 'align' => 'left', + 'width' => '200', 'search' => 'true', 'searchoptions' => [ 'dataInit' => 'date_pick_one_month', @@ -8847,6 +8858,7 @@ public static function getGridColumns( 'name' => 'users', 'index' => 'users', 'align' => 'left', + 'width' => '100', 'search' => 'false', ]; @@ -8871,6 +8883,7 @@ public static function getGridColumns( 'name' => 'status', 'index' => 'status', 'align' => 'left', + 'width' => '120', 'search' => 'true', 'stype' => 'select', // for the bottom bar @@ -10213,4 +10226,248 @@ public static function getAllUserIdsInSession(int $sessionId): array return $users; } + + /** + * Method to export sessions data as CSV + */ + public static function exportSessionsAsCSV(array $selectedSessions): void + { + $csvHeaders = []; + $csvHeaders[] = get_lang('Session name'); + $csvHeaders[] = get_lang('Session start date'); + $csvHeaders[] = get_lang('Session end date'); + $csvHeaders[] = get_lang('Course name'); + $csvHeaders[] = get_lang('Official code'); + + if (api_sort_by_first_name()) { + $csvHeaders[] = get_lang('First name'); + $csvHeaders[] = get_lang('Last name'); + } else { + $csvHeaders[] = get_lang('Last name'); + $csvHeaders[] = get_lang('First name'); + } + + $csvHeaders[] = get_lang('Login'); + $csvHeaders[] = get_lang('Training time'); + $csvHeaders[] = get_lang('Course progress'); + $csvHeaders[] = get_lang('Exercise progress'); + $csvHeaders[] = get_lang('Exercise average'); + $csvHeaders[] = get_lang('Score'); + $csvHeaders[] = get_lang('Score').' - '.get_lang('Best attempt'); + $csvHeaders[] = get_lang('Student_publication'); + $csvHeaders[] = get_lang('Messages'); + $csvHeaders[] = get_lang('Classes'); + $csvHeaders[] = get_lang('Registration date'); + $csvHeaders[] = get_lang('FirstLogin in course'); + $csvHeaders[] = get_lang('Latest login in course'); + $csvHeaders[] = get_lang('Lp finalization date'); + $csvHeaders[] = get_lang('Quiz finalization date'); + + $csvData = []; + + foreach ($selectedSessions as $sessionId) { + $courses = SessionManager::get_course_list_by_session_id($sessionId); + + if (!empty($courses)) { + foreach ($courses as $course) { + $courseCode = $course['course_code']; + $studentList = CourseManager::get_student_list_from_course_code( + $courseCode, + true, + $sessionId + ); + + $nbStudents = count($studentList); + $userIds = array_keys($studentList); + $csvContentInSession = TrackingCourseLog::getUserData( + null, + $nbStudents, + null, + null, + [], + false, + true, + $courseCode, + $sessionId, + true, + $userIds + ); + + if (!empty($csvContentInSession)) { + $csvData = array_merge($csvData, $csvContentInSession); + } + } + } + } + + if (!empty($csvData)) { + array_unshift($csvData, $csvHeaders); + $filename = 'export_session_courses_reports_complete_' . api_get_local_time(); + Export::arrayToCsv($csvData, $filename); + exit; + } + } + + /** + * Exports session data as a ZIP file with CSVs and sends it for download. + */ + public static function exportSessionsAsZip(array $sessionList): void + { + // Create a temporary ZIP file + $tempZipFile = api_get_path(SYS_ARCHIVE_PATH) . api_get_unique_id() . '.zip'; + $tempDir = dirname($tempZipFile); + + // Check if the directory exists and has write permissions + if (!is_dir($tempDir) || !is_writable($tempDir)) { + exit("The directory for creating the ZIP file does not exist or lacks write permissions: $tempDir"); + } + + // Create a new ZIP archive + $zip = new \ZipArchive(); + + // Try to open the ZIP file for writing + if ($zip->open($tempZipFile, \ZipArchive::CREATE | \ZipArchive::OVERWRITE) !== true) { + exit("Unable to open the ZIP file for writing: $tempZipFile"); + } + + $csvList = []; + + // Process each session in the list + foreach ($sessionList as $sessionItemId) { + $em = Database::getManager(); + $sessionRepository = $em->getRepository(Session::class); + $session = $sessionRepository->find($sessionItemId); + + if ($session->getNbrCourses() > 0) { + $courses = $session->getCourses(); + $courseList = []; + + // Collect courses for the session + foreach ($courses as $sessionRelCourse) { + $courseList[] = $sessionRelCourse->getCourse(); + } + + foreach ($courseList as $course) { + $courseId = $course->getId(); + $courseInfo = api_get_course_info_by_id($courseId); + $addExerciseOption = api_get_configuration_value('add_exercise_best_attempt_in_report'); + $sortByFirstName = api_sort_by_first_name(); + $bestScoreLabel = get_lang('Score') . ' - ' . get_lang('Best attempt'); + $courseCode = $courseInfo['code']; + + // Prepare CSV headers + $csvHeaders = []; + $csvHeaders[] = get_lang('Official code'); + + if ($sortByFirstName) { + $csvHeaders[] = get_lang('First name'); + $csvHeaders[] = get_lang('Last name'); + } else { + $csvHeaders[] = get_lang('Last name'); + $csvHeaders[] = get_lang('First name'); + } + + $csvHeaders[] = get_lang('Login'); + $csvHeaders[] = get_lang('Training time'); + $csvHeaders[] = get_lang('Course progress'); + $csvHeaders[] = get_lang('Exercise progress'); + $csvHeaders[] = get_lang('Exercise average'); + $csvHeaders[] = get_lang('Score'); + $csvHeaders[] = $bestScoreLabel; + + // Include exercise results if available + $exerciseResultHeaders = []; + if (!empty($addExerciseOption) && isset($addExerciseOption['courses']) && + isset($addExerciseOption['courses'][$courseCode])) { + foreach ($addExerciseOption['courses'][$courseCode] as $exerciseId) { + $exercise = new Exercise(); + $exercise->read($exerciseId); + if ($exercise->iid) { + $title = get_lang('Exercise') . ': ' . $exercise->get_formated_title(); + $csvHeaders[] = $title; + $exerciseResultHeaders[] = $title; + } + } + } + + // Add more fields to the CSV headers + $csvHeaders[] = get_lang('Student_publication'); + $csvHeaders[] = get_lang('Messages'); + $csvHeaders[] = get_lang('Classes'); + $csvHeaders[] = get_lang('Registration date'); + $csvHeaders[] = get_lang('First login in course'); + $csvHeaders[] = get_lang('Latest login in course'); + + // Get the list of students for the course + $studentList = CourseManager::get_student_list_from_course_code($courseCode, true, $sessionItemId); + $nbStudents = count($studentList); + + // Pass the necessary data as parameters instead of using globals + $userIds = array_keys($studentList); + + // Get the user data for CSV content + $csvContentInSession = TrackingCourseLog::getUserData( + null, + $nbStudents, + null, + null, + [], + true, + true, + $courseCode, + $sessionItemId, + true, + $userIds + ); + array_unshift($csvContentInSession, $csvHeaders); + + // Get session info and dates + $sessionInfo = api_get_session_info($sessionItemId); + $sessionDates = SessionManager::parseSessionDates($session); + + // Add session name and dates to the CSV content + array_unshift($csvContentInSession, [get_lang('Date'), $sessionDates['access']]); + array_unshift($csvContentInSession, [get_lang('SessionName'), Security::remove_XSS($sessionInfo['name'])]); + + // Prepare CSV file information + $csvList[] = [ + 'session_id' => $sessionItemId, + 'session_name' => $session->getTitle(), + 'course_id' => $courseId, + 'course_name' => $courseInfo['name'], + 'path' => Export::arrayToCsv($csvContentInSession, '', true), // Generate the CSV + ]; + } + } + } + + // Add the generated CSV files to the ZIP archive + foreach ($csvList as $csv) { + $newFileName = $csv['session_id'] . '_' . $csv['session_name'] . '-' . $csv['course_id'] . '_' . $csv['course_name'] . '.csv'; + + if (file_exists($csv['path'])) { + $zip->addFile($csv['path'], $newFileName); + } + } + + // Close the ZIP file + if ($zip->close() === false) { + exit("Could not close the ZIP file correctly."); + } + + // Clean up the CSV files after adding them to the ZIP + foreach ($csvList as $csv) { + if (file_exists($csv['path'])) { + unlink($csv['path']); + } + } + + // Send the ZIP file for download + if (file_exists($tempZipFile)) { + DocumentManager::file_send_for_download($tempZipFile, true); + unlink($tempZipFile); // Delete the temporary ZIP file after download + } else { + exit("The ZIP file was not created correctly."); + } + } } diff --git a/public/main/inc/lib/tracking.lib.php b/public/main/inc/lib/tracking.lib.php index 2fae9bbc3e2..b4daec58eb9 100644 --- a/public/main/inc/lib/tracking.lib.php +++ b/public/main/inc/lib/tracking.lib.php @@ -2640,7 +2640,12 @@ public static function get_exercise_student_average_best_attempt( $sessionId ) { $result = 0; - if (!empty($exercise_list)) { + + if ($exercise_list instanceof \Doctrine\ORM\QueryBuilder) { + $exercise_list = $exercise_list->getQuery()->getResult(); + } + + if (!empty($exercise_list) && (is_array($exercise_list) || $exercise_list instanceof \Countable)) { foreach ($exercise_list as $exercise_data) { $exercise_id = $exercise_data->getIid(); $best_attempt = Event::get_best_attempt_exercise_results_per_user( @@ -2654,8 +2659,11 @@ public static function get_exercise_student_average_best_attempt( $result += $best_attempt['score'] / $best_attempt['max_score']; } } - $result = $result / count($exercise_list); - $result = round($result, 2) * 100; + + if (count($exercise_list) > 0) { + $result = $result / count($exercise_list); + $result = round($result, 2) * 100; + } } return $result.'%'; @@ -8021,1274 +8029,131 @@ function ($columHeader, $key) use ($trackingColumns) { ['class' => 'table-responsive'] ); } -} -/** - * @todo move into a proper file - */ -class TrackingCourseLog -{ /** - * @return mixed - */ - public static function count_item_resources() - { - $session_id = api_get_session_id(); - $course_id = api_get_course_int_id(); - - $table_item_property = Database::get_course_table(TABLE_ITEM_PROPERTY); - $table_user = Database::get_main_table(TABLE_MAIN_USER); - - $sql = "SELECT count(tool) AS total_number_of_items - FROM $table_item_property track_resource, $table_user user - WHERE - track_resource.c_id = $course_id AND - track_resource.insert_user_id = user.id user_id AND - session_id ".(empty($session_id) ? ' IS NULL ' : " = $session_id "); - - if (isset($_GET['keyword'])) { - $keyword = Database::escape_string(trim($_GET['keyword'])); - $sql .= " AND ( - user.username LIKE '%".$keyword."%' OR - lastedit_type LIKE '%".$keyword."%' OR - tool LIKE '%".$keyword."%' - )"; - } - - $sql .= " AND tool IN ( - 'document', - 'learnpath', - 'quiz', - 'glossary', - 'link', - 'course_description', - 'announcement', - 'thematic', - 'thematic_advance', - 'thematic_plan' - )"; - $res = Database::query($sql); - $obj = Database::fetch_object($res); - - return $obj->total_number_of_items; - } - - /** - * @param $from - * @param $number_of_items - * @param $column - * @param $direction + * Counts the number of student publications for a given course, session, and group. * - * @return array - */ - public static function get_item_resources_data($from, $number_of_items, $column, $direction) - { - $session_id = api_get_session_id(); - $course_id = api_get_course_int_id(); - - $table_item_property = Database::get_course_table(TABLE_ITEM_PROPERTY); - $table_user = Database::get_main_table(TABLE_MAIN_USER); - $table_session = Database::get_main_table(TABLE_MAIN_SESSION); - $tblSessionRelUser = Database::get_main_table(TABLE_MAIN_SESSION_USER); - $column = (int) $column; - $direction = !in_array(strtolower(trim($direction)), ['asc', 'desc']) ? 'asc' : $direction; - - $sql = "SELECT - tool as col0, - lastedit_type as col1, - ref as ref, - user.username as col3, - insert_date as col6, - visibility as col7, - user.user_id as user_id - FROM $table_item_property track_resource, $table_user user - WHERE - track_resource.c_id = $course_id AND - track_resource.insert_user_id = user.user_id AND - session_id ".(empty($session_id) ? ' IS NULL ' : " = $session_id "); - - if (isset($_GET['keyword'])) { - $keyword = Database::escape_string(trim($_GET['keyword'])); - $sql .= " AND ( - user.username LIKE '%".$keyword."%' OR - lastedit_type LIKE '%".$keyword."%' OR - tool LIKE '%".$keyword."%' - ) "; - } - - $sql .= " AND tool IN ( - 'document', - 'learnpath', - 'quiz', - 'glossary', - 'link', - 'course_description', - 'announcement', - 'thematic', - 'thematic_advance', - 'thematic_plan' - )"; - - if (0 == $column) { - $column = '0'; - } - if ('' != $column && '' != $direction) { - if (2 != $column && 4 != $column) { - $sql .= " ORDER BY col$column $direction"; - } - } else { - $sql .= " ORDER BY col6 DESC "; - } - - $from = intval($from); - if ($from) { - $number_of_items = intval($number_of_items); - $sql .= " LIMIT $from, $number_of_items "; - } - - $res = Database::query($sql); - $resources = []; - $thematic_tools = ['thematic', 'thematic_advance', 'thematic_plan']; - while ($row = Database::fetch_array($res)) { - $ref = $row['ref']; - $table_name = self::get_tool_name_table($row['col0']); - $table_tool = Database::get_course_table($table_name['table_name']); - - $id = $table_name['id_tool']; - $recorset = false; - - if (in_array($row['col0'], ['thematic_plan', 'thematic_advance'])) { - $tbl_thematic = Database::get_course_table(TABLE_THEMATIC); - $sql = "SELECT thematic_id FROM $table_tool - WHERE c_id = $course_id AND iid = $ref"; - $rs_thematic = Database::query($sql); - if (Database::num_rows($rs_thematic)) { - $row_thematic = Database::fetch_array($rs_thematic); - $thematic_id = $row_thematic['thematic_id']; - - $sql = "SELECT s.id, s.title, u.username - FROM $tbl_thematic t - INNER JOIN $tblSessionRelUser sru - ON t.session_id = sru.session_id - INNER JOIN $table_session s - ON sru.session_id = s.id - INNER JOIN $table_user u - ON sru.user_id = u.id - WHERE - t.c_id = $course_id AND - t.id = $thematic_id AND - sru.relation_type = ".SessionEntity::GENERAL_COACH; - $recorset = Database::query($sql); - } - } else { - $sql = "SELECT s.id, s.title u.username - FROM c_tool t, session s, user u, $tblSessionRelUser sru - WHERE - t.c_id = $course_id AND - t.session_id = s.id AND - sru.session_id = s.id AND - sru.user_id = u.id AND - t.$id = $ref"; - $recorset = Database::query($sql); - } - - if (!empty($recorset)) { - $obj = Database::fetch_object($recorset); - - $name_session = ''; - $coach_name = ''; - if (!empty($obj)) { - $name_session = $obj->title; - $coach_name = $obj->username; - } - - $url_tool = api_get_path(WEB_CODE_PATH).$table_name['link_tool']; - $row[0] = ''; - if (2 != $row['col6']) { - if (in_array($row['col0'], $thematic_tools)) { - $exp_thematic_tool = explode('_', $row['col0']); - $thematic_tool_title = ''; - if (is_array($exp_thematic_tool)) { - foreach ($exp_thematic_tool as $exp) { - $thematic_tool_title .= api_ucfirst($exp); - } - } else { - $thematic_tool_title = api_ucfirst($row['col0']); - } - - $row[0] = ''.get_lang($thematic_tool_title).''; - } else { - $row[0] = ''.get_lang('Tool'.api_ucfirst($row['col0'])).''; - } - } else { - $row[0] = api_ucfirst($row['col0']); - } - $row[1] = get_lang($row[1]); - $row[6] = api_convert_and_format_date($row['col6'], null, date_default_timezone_get()); - $row[5] = ''; - //@todo Improve this code please - switch ($table_name['table_name']) { - case 'document': - $sql = "SELECT tool.title as title FROM $table_tool tool - WHERE c_id = $course_id AND iid = $ref"; - $rs_document = Database::query($sql); - $obj_document = Database::fetch_object($rs_document); - if ($obj_document) { - $row[5] = $obj_document->title; - } - break; - case 'announcement': - $sql = "SELECT title FROM $table_tool - WHERE c_id = $course_id AND id = $ref"; - $rs_document = Database::query($sql); - $obj_document = Database::fetch_object($rs_document); - if ($obj_document) { - $row[5] = $obj_document->title; - } - break; - case 'glossary': - $sql = "SELECT title FROM $table_tool - WHERE c_id = $course_id AND glossary_id = $ref"; - $rs_document = Database::query($sql); - $obj_document = Database::fetch_object($rs_document); - if ($obj_document) { - $row[5] = $obj_document->title; - } - break; - case 'lp': - $sql = "SELECT title - FROM $table_tool WHERE c_id = $course_id AND id = $ref"; - $rs_document = Database::query($sql); - $obj_document = Database::fetch_object($rs_document); - $row[5] = $obj_document->title; - break; - case 'quiz': - $sql = "SELECT title FROM $table_tool - WHERE c_id = $course_id AND id = $ref"; - $rs_document = Database::query($sql); - $obj_document = Database::fetch_object($rs_document); - if ($obj_document) { - $row[5] = $obj_document->title; - } - break; - case 'course_description': - $sql = "SELECT title FROM $table_tool - WHERE c_id = $course_id AND id = $ref"; - $rs_document = Database::query($sql); - $obj_document = Database::fetch_object($rs_document); - if ($obj_document) { - $row[5] = $obj_document->title; - } - break; - case 'thematic': - $rs = Database::query("SELECT title FROM $table_tool WHERE c_id = $course_id AND id = $ref"); - if (Database::num_rows($rs) > 0) { - $obj = Database::fetch_object($rs); - if ($obj) { - $row[5] = $obj->title; - } - } - break; - case 'thematic_advance': - $rs = Database::query("SELECT content FROM $table_tool WHERE c_id = $course_id AND id = $ref"); - if (Database::num_rows($rs) > 0) { - $obj = Database::fetch_object($rs); - if ($obj) { - $row[5] = $obj->content; - } - } - break; - case 'thematic_plan': - $rs = Database::query("SELECT title FROM $table_tool WHERE c_id = $course_id AND id = $ref"); - if (Database::num_rows($rs) > 0) { - $obj = Database::fetch_object($rs); - if ($obj) { - $row[5] = $obj->title; - } - } - break; - default: - break; - } - - $row2 = $name_session; - if (!empty($coach_name)) { - $row2 .= '
'.get_lang('Coach').': '.$coach_name; - } - $row[2] = $row2; - if (!empty($row['col3'])) { - $userInfo = api_get_user_info($row['user_id']); - $row['col3'] = Display::url( - $row['col3'], - $userInfo['profile_url'] - ); - $row[3] = $row['col3']; - - $ip = Tracking::get_ip_from_user_event( - $row['user_id'], - $row['col6'], - true - ); - if (empty($ip)) { - $ip = get_lang('Unknown'); - } - $row[4] = $ip; - } - - $resources[] = $row; - } - } - - return $resources; - } - - /** - * @param string $tool + * @param int $courseId + * @param int|null $sessionId + * @param int|null $groupId * - * @return array + * @return int The number of student publications. */ - public static function get_tool_name_table($tool) + public static function countStudentPublications(int $courseId, ?int $sessionId = null, ?int $groupId = null): int { - switch ($tool) { - case 'document': - $table_name = TABLE_DOCUMENT; - $link_tool = 'document/document.php'; - $id_tool = 'id'; - break; - case 'learnpath': - $table_name = TABLE_LP_MAIN; - $link_tool = 'lp/lp_controller.php'; - $id_tool = 'id'; - break; - case 'quiz': - $table_name = TABLE_QUIZ_TEST; - $link_tool = 'exercise/exercise.php'; - $id_tool = 'id'; - break; - case 'glossary': - $table_name = TABLE_GLOSSARY; - $link_tool = 'glossary/index.php'; - $id_tool = 'glossary_id'; - break; - case 'link': - $table_name = TABLE_LINK; - $link_tool = 'link/link.php'; - $id_tool = 'id'; - break; - case 'course_description': - $table_name = TABLE_COURSE_DESCRIPTION; - $link_tool = 'course_description/'; - $id_tool = 'id'; - break; - case 'announcement': - $table_name = TABLE_ANNOUNCEMENT; - $link_tool = 'announcements/announcements.php'; - $id_tool = 'id'; - break; - case 'thematic': - $table_name = TABLE_THEMATIC; - $link_tool = 'course_progress/index.php'; - $id_tool = 'id'; - break; - case 'thematic_advance': - $table_name = TABLE_THEMATIC_ADVANCE; - $link_tool = 'course_progress/index.php'; - $id_tool = 'id'; - break; - case 'thematic_plan': - $table_name = TABLE_THEMATIC_PLAN; - $link_tool = 'course_progress/index.php'; - $id_tool = 'id'; - break; - default: - $table_name = $tool; - break; - } + $repo = Container::getStudentPublicationRepository(); - return [ - 'table_name' => $table_name, - 'link_tool' => $link_tool, - 'id_tool' => $id_tool, - ]; - } + $course = api_get_course_entity($courseId); + $session = api_get_session_entity($sessionId); + $group = api_get_group_entity($groupId); - /** - * @return string - */ - public static function display_additional_profile_fields() - { - // getting all the extra profile fields that are defined by the platform administrator - $extra_fields = UserManager::get_extra_fields(0, 50, 5, 'ASC'); - - // creating the form - $return = '
'; - - // the select field with the additional user profile fields (= this is where we select the field of which we want to see - // the information the users have entered or selected. - $return .= ''; + $qb = $repo->getResourcesByCourse($course, $session, $group); + $qb->select('COUNT(resource.iid)'); - // the form elements for the $_GET parameters (because the form is passed through GET - foreach ($_GET as $key => $value) { - if ('additional_profile_field' != $key) { - $return .= ''; - } - } - // the submit button - $return .= ''; - $return .= '
'; - if ($extra_fields_to_show > 0) { - return $return; - } else { - return ''; - } + return (int) $qb->getQuery()->getSingleScalarResult(); } /** - * This function gets all the information of a certrain ($field_id) - * additional profile field for a specific list of users is more efficent - * than get_addtional_profile_information_of_field() function - * It gets the information of all the users so that it can be displayed - * in the sortable table or in the csv or xls export. - * - * @author Julio Montoya - * - * @param int field id - * @param array list of user ids + * Counts the number of forum posts for a given course, session, and group. * - * @return array - * - * @since Nov 2009 + * @param int $courseId + * @param int|null $sessionId + * @param int|null $groupId * - * @version 1.8.6.2 + * @return int The number of forum posts. */ - public static function getAdditionalProfileInformationOfFieldByUser($field_id, $users) + public static function countStudentMessages(int $courseId, ?int $sessionId = null, ?int $groupId = null): int { - // Database table definition - $table_user = Database::get_main_table(TABLE_MAIN_USER); - $table_user_field_values = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES); - $extraField = Database::get_main_table(TABLE_EXTRA_FIELD); - $result_extra_field = UserManager::get_extra_field_information($field_id); - $return = []; - if (!empty($users)) { - if (UserManager::USER_FIELD_TYPE_TAG == $result_extra_field['value_type']) { - foreach ($users as $user_id) { - $user_result = UserManager::get_user_tags($user_id, $field_id); - $tag_list = []; - foreach ($user_result as $item) { - $tag_list[] = $item['tag']; - } - $return[$user_id][] = implode(', ', $tag_list); - } - } else { - $new_user_array = []; - foreach ($users as $user_id) { - $new_user_array[] = "'".$user_id."'"; - } - $users = implode(',', $new_user_array); - $extraFieldType = EntityExtraField::USER_FIELD_TYPE; - // Selecting only the necessary information NOT ALL the user list - $sql = "SELECT user.id as user_id, v.value - FROM $table_user user - INNER JOIN $table_user_field_values v - ON (user.id = v.item_id) - INNER JOIN $extraField f - ON (f.id = v.field_id) - WHERE - f.item_type = $extraFieldType AND - v.field_id=".intval($field_id)." AND - user.id IN ($users)"; - - $result = Database::query($sql); - while ($row = Database::fetch_array($result)) { - // get option value for field type double select by id - if (!empty($row['value'])) { - if (ExtraField::FIELD_TYPE_DOUBLE_SELECT == - $result_extra_field['value_type'] - ) { - $id_double_select = explode(';', $row['value']); - if (is_array($id_double_select)) { - $value1 = $result_extra_field['options'][$id_double_select[0]]['option_value']; - $value2 = $result_extra_field['options'][$id_double_select[1]]['option_value']; - $row['value'] = ($value1.';'.$value2); - } - } - - if (ExtraField::FIELD_TYPE_SELECT_WITH_TEXT_FIELD == $result_extra_field['value_type']) { - $parsedValue = explode('::', $row['value']); - - if ($parsedValue) { - $value1 = $result_extra_field['options'][$parsedValue[0]]['display_text']; - $value2 = $parsedValue[1]; - - $row['value'] = "$value1: $value2"; - } - } - - if (ExtraField::FIELD_TYPE_TRIPLE_SELECT == $result_extra_field['value_type']) { - [$level1, $level2, $level3] = explode(';', $row['value']); - - $row['value'] = $result_extra_field['options'][$level1]['display_text'].' / '; - $row['value'] .= $result_extra_field['options'][$level2]['display_text'].' / '; - $row['value'] .= $result_extra_field['options'][$level3]['display_text']; - } - } - // get other value from extra field - $return[$row['user_id']][] = $row['value']; - } - } - } - - return $return; - } - - /** - * count the number of students in this course (used for SortableTable) - * Deprecated. - */ - public function count_student_in_course() - { - global $nbStudents; - - return $nbStudents; - } - - public function sort_users($a, $b) - { - $tracking = Session::read('tracking_column'); - - return strcmp( - trim(api_strtolower($a[$tracking])), - trim(api_strtolower($b[$tracking])) - ); - } - - public function sort_users_desc($a, $b) - { - $tracking = Session::read('tracking_column'); + $repo = Container::getForumPostRepository(); - return strcmp( - trim(api_strtolower($b[$tracking])), - trim(api_strtolower($a[$tracking])) - ); - } + $course = api_get_course_entity($courseId); + $session = api_get_session_entity($sessionId); + $group = api_get_group_entity($groupId); - /** - * Get number of users for sortable with pagination. - * - * @return int - */ - public static function get_number_of_users($conditions) - { - $conditions['get_count'] = true; + $qb = $repo->getResourcesByCourse($course, $session, $group); + $qb->select('COUNT(resource.iid)'); - return self::get_user_data(null, null, null, null, $conditions); + return (int) $qb->getQuery()->getSingleScalarResult(); } /** - * Get data for users list in sortable with pagination. + * It gets the last finalization date of learnpaths in a course. * - * @param int $from - * @param int $number_of_items - * @param $column - * @param $direction - * @param $conditions - * - * @return array + * @return string finalization date formatted or false if it is empty. */ - public static function get_user_data( - $from, - $number_of_items, - $column, - $direction, - $conditions = [], - $options = [] + public static function getCourseLpFinalizationDate( + int $userId, + int $courseId, + int $sessionId, + bool $convertDate = true ) { - global $user_ids, $export_csv, $sessionId; - $includeInvitedUsers = $conditions['include_invited_users']; // include the invited users - $getCount = isset($conditions['get_count']) ? $conditions['get_count'] : false; - - $course = api_get_course_entity($conditions['course_id']); - $courseId = $course->getId(); - $courseCode = $course->getCode(); - - $csv_content = []; - $tbl_user = Database::get_main_table(TABLE_MAIN_USER); - $tbl_url_rel_user = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER); - $access_url_id = api_get_current_access_url_id(); - - // get all users data from a course for sortable with limit - if (is_array($user_ids)) { - $user_ids = array_map('intval', $user_ids); - $condition_user = " WHERE user.id IN (".implode(',', $user_ids).") "; - } else { - $user_ids = (int) $user_ids; - $condition_user = " WHERE user.id = $user_ids "; - } - - if (!empty($_GET['user_keyword'])) { - $keyword = trim(Database::escape_string($_GET['user_keyword'])); - $condition_user .= " AND ( - user.firstname LIKE '%".$keyword."%' OR - user.lastname LIKE '%".$keyword."%' OR - user.username LIKE '%".$keyword."%' OR - user.email LIKE '%".$keyword."%' - ) "; - } - - $url_table = ''; - $url_condition = ''; - if (api_is_multiple_url_enabled()) { - $url_table = " INNER JOIN $tbl_url_rel_user as url_users ON (user.id = url_users.user_id)"; - $url_condition = " AND access_url_id = '$access_url_id'"; - } - - $invitedUsersCondition = ''; - if (!$includeInvitedUsers) { - $invitedUsersCondition = " AND user.status != ".INVITEE; - } - - $select = ' - SELECT user.id as user_id, - user.official_code as col0, - user.lastname as col1, - user.firstname as col2, - user.username as col3, - user.email as col4'; - if ($getCount) { - $select = ' SELECT COUNT(distinct(user.id)) as count '; - } - - $sqlInjectJoins = ''; - $where = 'AND 1 = 1 '; - $sqlInjectWhere = ''; - if (!empty($conditions)) { - if (isset($conditions['inject_joins'])) { - $sqlInjectJoins = $conditions['inject_joins']; - } - if (isset($conditions['where'])) { - $where = $conditions['where']; - } - if (isset($conditions['inject_where'])) { - $sqlInjectWhere = $conditions['inject_where']; - } - $injectExtraFields = !empty($conditions['inject_extra_fields']) ? $conditions['inject_extra_fields'] : 1; - $injectExtraFields = rtrim($injectExtraFields, ', '); - if (false === $getCount) { - $select .= " , $injectExtraFields"; - } - } - - $sql = "$select - FROM $tbl_user as user - $url_table - $sqlInjectJoins - $condition_user - $url_condition - $invitedUsersCondition - $where - $sqlInjectWhere - "; - - if (!in_array($direction, ['ASC', 'DESC'])) { - $direction = 'ASC'; - } - - $column = (int) $column; - $from = (int) $from; - $number_of_items = (int) $number_of_items; - - if ($getCount) { - $res = Database::query($sql); - $row = Database::fetch_array($res); - - return $row['count']; - } - - $sql .= " ORDER BY col$column $direction "; - $sql .= " LIMIT $from, $number_of_items"; - - $res = Database::query($sql); - $users = []; - - $total_surveys = 0; - /*$total_exercises = ExerciseLib::get_all_exercises( - $courseInfo, - $session_id, - false, - null, - false, - 3 - );*/ - $session = api_get_session_entity($sessionId); - $repo = Container::getQuizRepository(); - $qb = $repo->findAllByCourse($course, $session, null, 2); - $exercises = $qb->getQuery()->getResult(); - - if (empty($sessionId)) { - $survey_user_list = []; - // @todo - //$surveyList = SurveyManager::get_surveys($courseCode, $session_id); - $surveyList = []; - if ($surveyList) { - $total_surveys = count($surveyList); - foreach ($surveyList as $survey) { - $user_list = SurveyManager::get_people_who_filled_survey( - $survey['survey_id'], - false, - $courseId - ); - - foreach ($user_list as $user_id) { - isset($survey_user_list[$user_id]) ? $survey_user_list[$user_id]++ : $survey_user_list[$user_id] = 1; - } - } - } - } - - $urlBase = api_get_path(WEB_CODE_PATH).'my_space/myStudents.php?details=true&cid='.$courseId. - '&origin=tracking_course&sid='.$sessionId; - - $sortByFirstName = api_sort_by_first_name(); - Session::write('user_id_list', []); - $userIdList = []; - $addExerciseOption = api_get_setting('exercise.add_exercise_best_attempt_in_report', true); - $exerciseResultsToCheck = []; - if (!empty($addExerciseOption) && isset($addExerciseOption['courses']) && - isset($addExerciseOption['courses'][$courseCode]) - ) { - foreach ($addExerciseOption['courses'][$courseCode] as $exerciseId) { - $exercise = new Exercise(); - $exercise->read($exerciseId); - if ($exercise->iId) { - $exerciseResultsToCheck[] = $exercise; - } - } - } - while ($user = Database::fetch_assoc($res)) { - $userId = $user['user_id']; - $userIdList[] = $userId; - $userEntity = api_get_user_entity($userId); - $user['official_code'] = $user['col0']; - $user['username'] = $user['col3']; - $user['time'] = api_time_to_hms( - Tracking::get_time_spent_on_the_course( - $user['user_id'], - $courseId, - $sessionId - ) - ); - - $avg_student_score = Tracking::get_avg_student_score( - $userId, - $course, - [], - $session - ); - - $averageBestScore = Tracking::get_avg_student_score( - $user['user_id'], - $course, - [], - $session, - false, - false, - true - ); - - $avg_student_progress = Tracking::get_avg_student_progress( - $user['user_id'], - $course, - [], - $session - ); - - if (empty($avg_student_progress)) { - $avg_student_progress = 0; - } - $user['average_progress'] = $avg_student_progress.'%'; - - $total_user_exercise = Tracking::get_exercise_student_progress( - $exercises, - $user['user_id'], - $courseId, - $sessionId - ); - - $user['exercise_progress'] = $total_user_exercise; - - $total_user_exercise = Tracking::get_exercise_student_average_best_attempt( - $exercises, - $user['user_id'], - $courseId, - $sessionId - ); - - $user['exercise_average_best_attempt'] = $total_user_exercise; - - if (is_numeric($avg_student_score)) { - $user['student_score'] = $avg_student_score.'%'; - } else { - $user['student_score'] = $avg_student_score; - } - - if (is_numeric($averageBestScore)) { - $user['student_score_best'] = $averageBestScore.'%'; - } else { - $user['student_score_best'] = $averageBestScore; - } - - $exerciseResults = []; - if (!empty($exerciseResultsToCheck)) { - foreach ($exerciseResultsToCheck as $exercise) { - $bestExerciseResult = Event::get_best_attempt_exercise_results_per_user( - $user['user_id'], - $exercise->iId, - $courseId, - $sessionId, - false - ); - - $best = null; - if ($bestExerciseResult) { - $best = $bestExerciseResult['score'] / $bestExerciseResult['max_score']; - $best = round($best, 2) * 100; - $best .= '%'; - } - $exerciseResults['exercise_'.$exercise->iId] = $best; - } - } - $user['count_assignments'] = Container::getStudentPublicationRepository()->countUserPublications( - $userEntity, - $course, - $session - ); - $user['count_messages'] = Container::getForumPostRepository()->countUserForumPosts( - $userEntity, - $course, - $session - ); - $user['first_connection'] = Tracking::get_first_connection_date_on_the_course( - $user['user_id'], - $courseId, - $sessionId, - false === $export_csv - ); - - $user['last_connection'] = Tracking::get_last_connection_date_on_the_course( - $user['user_id'], - ['real_id' => $course->getId()], - $sessionId, - false === $export_csv - ); - - if ($export_csv) { - if (!empty($user['first_connection'])) { - $user['first_connection'] = api_get_local_time($user['first_connection']); - } else { - $user['first_connection'] = '-'; - } - if (!empty($user['last_connection'])) { - $user['last_connection'] = api_get_local_time($user['last_connection']); - } else { - $user['last_connection'] = '-'; - } - } - - if (empty($sessionId)) { - $user['survey'] = (isset($survey_user_list[$user['user_id']]) ? $survey_user_list[$user['user_id']] : 0).' / '.$total_surveys; - } - - $url = $urlBase.'&student='.$user['user_id']; - - $user['link'] = '
- '.Display::getMdiIcon( - 'fast-forward-outline', - 'ch-tool-icon', - null, - ICON_SIZE_MEDIUM, - get_lang('Details') - ).'
'; - - // store columns in array $users - $user_row = []; - $user_row['official_code'] = $user['official_code']; //0 - if ($sortByFirstName) { - $user_row['firstname'] = $user['col2']; - $user_row['lastname'] = $user['col1']; - } else { - $user_row['lastname'] = $user['col1']; - $user_row['firstname'] = $user['col2']; - } - $user_row['username'] = $user['username']; - $user_row['time'] = $user['time']; - $user_row['average_progress'] = $user['average_progress']; - $user_row['exercise_progress'] = $user['exercise_progress']; - $user_row['exercise_average_best_attempt'] = $user['exercise_average_best_attempt']; - $user_row['student_score'] = $user['student_score']; - $user_row['student_score_best'] = $user['student_score_best']; - if (!empty($exerciseResults)) { - foreach ($exerciseResults as $exerciseId => $bestResult) { - $user_row[$exerciseId] = $bestResult; - } - } - $user_row['count_assignments'] = $user['count_assignments']; - $user_row['count_messages'] = $user['count_messages']; - - $userGroupManager = new UserGroupModel(); - $user_row['classes'] = $userGroupManager->getLabelsFromNameList($user['user_id'], Usergroup::NORMAL_CLASS); - - if (empty($sessionId)) { - $user_row['survey'] = $user['survey']; - } else { - $userSession = SessionManager::getUserSession($user['user_id'], $sessionId); - $user_row['registered_at'] = ''; - if ($userSession) { - $user_row['registered_at'] = api_get_local_time($userSession['registered_at']); - } - } - - $user_row['first_connection'] = $user['first_connection']; - $user_row['last_connection'] = $user['last_connection']; - - // we need to display an additional profile field - if (isset($_GET['additional_profile_field'])) { - $data = Session::read('additional_user_profile_info'); - - $extraFieldInfo = Session::read('extra_field_info'); - foreach ($_GET['additional_profile_field'] as $fieldId) { - if (isset($data[$fieldId]) && isset($data[$fieldId][$user['user_id']])) { - if (is_array($data[$fieldId][$user['user_id']])) { - $user_row[$extraFieldInfo[$fieldId]['variable']] = implode( - ', ', - $data[$fieldId][$user['user_id']] - ); - } else { - $user_row[$extraFieldInfo[$fieldId]['variable']] = $data[$fieldId][$user['user_id']]; - } - } else { - $user_row[$extraFieldInfo[$fieldId]['variable']] = ''; - } - } - } - - if ('true' === api_get_setting('show_email_addresses')) { - $user_row['email'] = $user['col4']; - } - - $user_row['link'] = $user['link']; + $tblLpView = Database::get_course_table(TABLE_LP_VIEW); + $tblLpItem = Database::get_course_table(TABLE_LP_ITEM); + $tblLpItemView = Database::get_course_table(TABLE_LP_ITEM_VIEW); + + $sql = "SELECT FROM_UNIXTIME(liv.start_time) as start_date + FROM $tblLpItemView liv + INNER JOIN + $tblLpView lv ON lv.iid = liv.lp_view_id + INNER JOIN + $tblLpItem li ON li.iid = liv.lp_item_id + WHERE + lv.user_id = $userId AND + lv.c_id = $courseId AND + lv.session_id = $sessionId AND + li.item_type = '".TOOL_LP_FINAL_ITEM."' AND + liv.status = 'completed' + ORDER BY start_date DESC + LIMIT 1"; - if ($export_csv) { - if (empty($sessionId)) { - unset($user_row['classes']); - unset($user_row['link']); - } else { - unset($user_row['classes']); - unset($user_row['link']); - } + $rs = Database::query($sql); + $lpFinalDate = Database::result($rs, 0, 0); - $csv_content[] = $user_row; - } - $users[] = array_values($user_row); + if (empty($lpFinalDate)) { + return false; } - if ($export_csv) { - Session::write('csv_content', $csv_content); + if ($convertDate) { + return api_convert_and_format_date($lpFinalDate, DATE_FORMAT_SHORT); } - Session::erase('additional_user_profile_info'); - Session::erase('extra_field_info'); - Session::write('user_id_list', $userIdList); - - return $users; + return $lpFinalDate; } /** - * Get data for users list in sortable with pagination. - * - * @param $from - * @param $number_of_items - * @param $column - * @param $direction - * @param $includeInvitedUsers boolean Whether include the invited users + * It gets the last finalization date of exercises in a course. * - * @return array + * @return string finalization date formatted or false if it is empty. */ - public static function getTotalTimeReport( - $from, - $number_of_items, - $column, - $direction, - $params = [] + public static function getCourseQuizLastFinalizationDate( + int $userId, + int $courseId, + int $sessionId, + bool $convertDate = true ) { - global $user_ids, $course_code, $export_csv, $sessionId; - $includeInvitedUsers = false; - $courseId = $params['cid']; - $sessionId = $params['sid']; + $tblTrackExercise = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES); - $course_code = Database::escape_string($course_code); - $tbl_user = Database::get_main_table(TABLE_MAIN_USER); - $tbl_url_rel_user = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER); - $access_url_id = api_get_current_access_url_id(); - - // get all users data from a course for sortable with limit - if (is_array($user_ids)) { - $user_ids = array_map('intval', $user_ids); - $condition_user = " WHERE user.user_id IN (".implode(',', $user_ids).") "; - } else { - $user_ids = (int) $user_ids; - $condition_user = " WHERE user.user_id = $user_ids "; - } - - $url_table = null; - $url_condition = null; - if (api_is_multiple_url_enabled()) { - $url_table = ", ".$tbl_url_rel_user." as url_users"; - $url_condition = " AND user.user_id = url_users.user_id AND access_url_id='$access_url_id'"; - } + $sql = "SELECT ex.exe_date + FROM $tblTrackExercise AS ex + WHERE + ex.c_id = $courseId AND + ex.session_id = $sessionId AND + ex.exe_user_id = $userId AND + ex.status = '' + ORDER BY ex.exe_date DESC + LIMIT 1"; + $rs = Database::query($sql); + $exeDate = Database::result($rs, 0, 0); - $invitedUsersCondition = ''; - if (!$includeInvitedUsers) { - $invitedUsersCondition = " AND user.status != ".INVITEE; + if (empty($exeDate)) { + return false; } - $sql = "SELECT - user.user_id as user_id, - user.official_code as col0, - user.lastname as col1, - user.firstname as col2, - user.username as col3 - FROM $tbl_user as user $url_table - $condition_user $url_condition $invitedUsersCondition"; - - if (!in_array($direction, ['ASC', 'DESC'])) { - $direction = 'ASC'; + if ($convertDate) { + return api_convert_and_format_date($exeDate, DATE_FORMAT_SHORT); } - $column = (int) $column; - $from = (int) $from; - $number_of_items = (int) $number_of_items; - - $sql .= " ORDER BY col$column $direction "; - $sql .= " LIMIT $from,$number_of_items"; - - $res = Database::query($sql); - $users = []; - - $sortByFirstName = api_sort_by_first_name(); - $course = api_get_course_entity($courseId); - - while ($user = Database::fetch_assoc($res)) { - $user['official_code'] = $user['col0']; - $user['lastname'] = $user['col1']; - $user['firstname'] = $user['col2']; - $user['username'] = $user['col3']; - - $totalCourseTime = Tracking::get_time_spent_on_the_course( - $user['user_id'], - $courseId, - $sessionId - ); - - $user['time'] = api_time_to_hms($totalCourseTime); - $totalLpTime = Tracking::get_time_spent_in_lp( - $user['user_id'], - $course, - [], - $sessionId - ); - - $user['total_lp_time'] = $totalLpTime; - $warning = ''; - if ($totalLpTime > $totalCourseTime) { - $warning = ' '.Display::label(get_lang('Time difference'), 'danger'); - } - - $user['total_lp_time'] = api_time_to_hms($totalLpTime).$warning; - $user['first_connection'] = Tracking::get_first_connection_date_on_the_course( - $user['user_id'], - $courseId, - $sessionId - ); - $user['last_connection'] = Tracking::get_last_connection_date_on_the_course( - $user['user_id'], - $courseInfo, - $sessionId, - false === $export_csv - ); - - $user['link'] = ' -
- - '.Display::getMdiIcon( - 'fast-forward-outline', - 'ch-tool-icon', - null, - ICON_SIZE_SMALL, - get_lang('Details') - ).' -
'; - - // store columns in array $users - $user_row = []; - $user_row['official_code'] = $user['official_code']; //0 - if ($sortByFirstName) { - $user_row['firstname'] = $user['firstname']; - $user_row['lastname'] = $user['lastname']; - } else { - $user_row['lastname'] = $user['lastname']; - $user_row['firstname'] = $user['firstname']; - } - $user_row['username'] = $user['username']; - $user_row['time'] = $user['time']; - $user_row['total_lp_time'] = $user['total_lp_time']; - $user_row['first_connection'] = $user['first_connection']; - $user_row['last_connection'] = $user['last_connection']; - $user_row['link'] = $user['link']; - $users[] = array_values($user_row); - } - - return $users; + return $exeDate; } - /** - * @param string $current - */ - public static function actionsLeft($current, $sessionId = 0, $addWrapper = true) - { - $usersLink = Display::url( - Display::getMdiIcon('account', 'ch-tool-icon', null, ICON_SIZE_MEDIUM, get_lang('Report on learners')), - 'courseLog.php?'.api_get_cidreq(true, false) - ); - - $groupsLink = Display::url( - Display::getMdiIcon('account-group', 'ch-tool-icon', null, ICON_SIZE_MEDIUM, get_lang('Group reporting')), - 'course_log_groups.php?'.api_get_cidreq() - ); - $resourcesLink = ''; - /*$resourcesLink = Display::url( - Display::getMdiIcon('chart-box', 'ch-tool-icon', null, ICON_SIZE_MEDIUM, get_lang('Report on resource')), - 'course_log_resources.php?'.api_get_cidreq(true, false) - );*/ - - $courseLink = Display::url( - Display::getMdiIcon('book-open-page-variant ', 'ch-tool-icon', null, ICON_SIZE_MEDIUM, get_lang('Course report')), - 'course_log_tools.php?'.api_get_cidreq(true, false) - ); - - $examLink = Display::url( - Display::getMdiIcon('chart-box', 'ch-tool-icon', null, ICON_SIZE_MEDIUM, get_lang('Exam tracking')), - api_get_path(WEB_CODE_PATH).'tracking/exams.php?'.api_get_cidreq() - ); - - $eventsLink = Display::url( - Display::getMdiIcon('security', 'ch-tool-icon', null, ICON_SIZE_MEDIUM, get_lang('Audit report')), - api_get_path(WEB_CODE_PATH).'tracking/course_log_events.php?'.api_get_cidreq() - ); - - $lpLink = Display::url( - Display::getMdiIcon('map-marker-path', 'ch-tool-icon', null, ICON_SIZE_MEDIUM, get_lang('CourseLPsGenericStats')), - api_get_path(WEB_CODE_PATH).'tracking/lp_report.php?'.api_get_cidreq() - ); - - $attendanceLink = ''; - if (!empty($sessionId)) { - $sessionInfo = api_get_session_info($sessionId); - $startDate = $sessionInfo['access_start_date']; - $endDate = $sessionInfo['access_end_date']; - $attendance = new Attendance(); - $checkExport = $attendance->getAttendanceLogin($startDate, $endDate); - if (false !== $checkExport) { - $attendanceLink = Display::url( - Display::getMdiIcon('av-timer', 'ch-tool-icon', null, ICON_SIZE_MEDIUM, get_lang('Logins')), - api_get_path(WEB_CODE_PATH).'attendance/index.php?'.api_get_cidreq().'&action=calendar_logins' - ); - } - } - - switch ($current) { - case 'users': - $usersLink = Display::url( - Display::getMdiIcon('account', 'ch-tool-icon-disabled', null, ICON_SIZE_MEDIUM, get_lang('Report on learners')), - '#' - ); - break; - case 'groups': - $groupsLink = Display::url( - Display::getMdiIcon('account-group', 'ch-tool-icon-disabled', null, ICON_SIZE_MEDIUM, get_lang('Group reporting')), - '#' - ); - break; - case 'courses': - $courseLink = Display::url( - Display::getMdiIcon('book-open-page-variant', 'ch-tool-icon-disabled', null, ICON_SIZE_MEDIUM, get_lang('Course report')), - '#' - ); - break; - case 'resources': - $resourcesLink = Display::url( - Display::getMdiIcon('package-variant-closed', 'ch-tool-icon-disabled', null, ICON_SIZE_MEDIUM, get_lang('Report on resource')), - '#' - ); - break; - case 'exams': - $examLink = Display::url( - Display::getMdiIcon('order-bool-ascending-variant', 'ch-tool-icon-disabled', null, ICON_SIZE_MEDIUM, get_lang('Exam tracking')), - '#' - ); - break; - case 'logs': - $eventsLink = Display::url( - Display::getMdiIcon('security', 'ch-tool-icon-disabled', null, ICON_SIZE_MEDIUM, get_lang('Audit report')), - '#' - ); - break; - case 'attendance': - if (!empty($sessionId)) { - $attendanceLink = Display::url( - Display::getMdiIcon('av-timer', 'ch-tool-icon-disabled', null, ICON_SIZE_MEDIUM, get_lang('Logins')), - '#' - ); - } - break; - case 'lp': - $lpLink = Display::url( - Display::getMdiIcon('map-marker-path', 'ch-tool-icon-disabled', null, ICON_SIZE_MEDIUM, get_lang('CourseLPsGenericStats')), - '#' - ); - break; - } - - $links = - $usersLink. - $groupsLink. - $courseLink. - $resourcesLink. - $examLink. - $eventsLink. - $lpLink. - $attendanceLink - ; - - if ($addWrapper) { - return Display::toolbarAction('tracking', [$links]); - } - - return $links; - } } diff --git a/public/main/inc/lib/usergroup.lib.php b/public/main/inc/lib/usergroup.lib.php index b4a4405a351..0055d602cc1 100644 --- a/public/main/inc/lib/usergroup.lib.php +++ b/public/main/inc/lib/usergroup.lib.php @@ -18,6 +18,8 @@ */ class UserGroupModel extends Model { + public const SOCIAL_CLASS = 1; + public const NORMAL_CLASS = 0; public $columns = [ 'id', 'title', diff --git a/public/main/session/session_list.php b/public/main/session/session_list.php index 31a8cae46ca..1ebe175b831 100644 --- a/public/main/session/session_list.php +++ b/public/main/session/session_list.php @@ -18,9 +18,21 @@ $action = $_REQUEST['action'] ?? null; $idChecked = $_REQUEST['idChecked'] ?? null; +$idMultiple = $_REQUEST['id'] ?? null; $listType = isset($_REQUEST['list_type']) ? Security::remove_XSS($_REQUEST['list_type']) : SessionManager::getDefaultSessionTab(); +$copySessionContent = isset($_REQUEST['copy_session_content']) ? true : false; switch ($action) { + case 'delete_multiple': + $sessionList = explode(',', $idMultiple); + foreach ($sessionList as $id) { + $sessionInfo = api_get_session_info($id); + if ($sessionInfo) { + $response = SessionManager::delete($id); + } + } + echo 1; + exit; case 'delete': $sessionInfo = api_get_session_info($idChecked); if ($sessionInfo) { @@ -37,7 +49,6 @@ } header('Location: '.$url); exit(); - break; case 'copy': $result = SessionManager::copy($idChecked); if ($result) { @@ -50,6 +61,32 @@ $url = 'session_list.php?list_type='.$listType; } header('Location: '.$url); + exit; + case 'copy_multiple': + $sessionList = explode(',', $idMultiple); + foreach ($sessionList as $id) { + $sessionIdCopied = SessionManager::copy($id); + if ($sessionIdCopied) { + $sessionInfo = api_get_session_info($sessionIdCopied); + Display::addFlash(Display::return_message(get_lang('ItemCopied').' - '.$sessionInfo['name'])); + } else { + Display::addFlash(Display::return_message(get_lang('ThereWasAnError'), 'error')); + } + } + $url = 'session_list.php'; + if ('custom' !== $listType) { + $url = 'session_list.php?list_type='.$listType; + } + header('Location: '.$url); + exit; + case 'export_csv': + $selectedSessions = explode(',', $idMultiple); + SessionManager::exportSessionsAsCSV($selectedSessions); + break; + + case 'export_multiple': + $sessionList = explode(',', $idMultiple); + SessionManager::exportSessionsAsZip($sessionList); break; } @@ -163,6 +200,11 @@ $urlAjaxExtraField = api_get_path(WEB_AJAX_PATH).'extra_field.ajax.php?1=1'; $orderUrl = api_get_path(WEB_AJAX_PATH).'session.ajax.php?a=order'; +$deleteUrl = api_get_self().'?list_type='.$listType.'&action=delete_multiple'; +$copyUrl = api_get_self().'?list_type='.$listType.'&action=copy_multiple'; +$exportUrl = api_get_self().'?list_type='.$listType.'&action=export_multiple'; +$exportCsvUrl = api_get_self().'?list_type='.$listType.'&action=export_csv'; +$extra_params['multiselect'] = true; ?> "; $htmlHeadXtra[] = $js; @@ -141,6 +175,7 @@ function(index) { $table_user = Database::get_main_table(TABLE_MAIN_USER); $TABLEQUIZ = Database::get_course_table(TABLE_QUIZ_TEST); +$userEditionExtraFieldToCheck = 'true' === api_get_setting('platform.user_edition_extra_field_to_check'); // Breadcrumbs. if ('resume_session' === $origin) { $interbreadcrumb[] = [ @@ -195,6 +230,31 @@ function(index) { Session::write('additional_user_profile_info', $userProfileInfo); Session::write('extra_field_info', $extra_info); +$defaultExtraFields = []; +$defaultExtraFieldsFromSettings = []; +$defaultExtraFieldsFromSettings = api_get_setting('course.course_log_default_extra_fields', true); +if (!empty($defaultExtraFieldsFromSettings) && isset($defaultExtraFieldsFromSettings['extra_fields'])) { + $defaultExtraFields = $defaultExtraFieldsFromSettings['extra_fields']; + $defaultExtraInfo = []; + $defaultUserProfileInfo = []; + + foreach ($defaultExtraFields as $fieldName) { + $extraFieldInfo = UserManager::get_extra_field_information_by_name($fieldName); + + if (!empty($extraFieldInfo)) { + // Fetching only the user that are loaded NOT ALL user in the portal. + $defaultUserProfileInfo[$extraFieldInfo['id']] = TrackingCourseLog::getAdditionalProfileInformationOfFieldByUser( + $extraFieldInfo['id'], + $user_ids + ); + $defaultExtraInfo[$extraFieldInfo['id']] = $extraFieldInfo; + } + } + + Session::write('default_additional_user_profile_info', $defaultUserProfileInfo); + Session::write('default_extra_field_info', $defaultExtraInfo); +} + Display::display_header($nameTools, 'Tracking'); $actionsLeft = TrackingCourseLog::actionsLeft('users', $sessionId, false); @@ -202,13 +262,6 @@ function(index) { $actionsRight = ''. Display::getMdiIcon(ActionIcon::PRINT, 'ch-tool-icon', null, ICON_SIZE_MEDIUM, get_lang('Print')).''; -$additionalParams = ''; -if (isset($_GET['additional_profile_field'])) { - foreach ($_GET['additional_profile_field'] as $fieldId) { - $additionalParams .= '&additional_profile_field[]='.(int) $fieldId; - } -} - $users_tracking_per_page = ''; if (isset($_GET['users_tracking_per_page'])) { $users_tracking_per_page = '&users_tracking_per_page='.intval($_GET['users_tracking_per_page']); @@ -326,8 +379,10 @@ function(index) { //$classes = $class->getUserGroupInCourse($options); $classes = $class->get_all(); +$bestScoreLabel = get_lang('Score').' - '.get_lang('BestAttempt'); + // Show the charts part only if there are students subscribed to this course/session -if ($nbStudents > 0) { +if ($nbStudents > 0 || isset($parameters['user_active'])) { // Classes $formClass = new FormValidator( 'classes', @@ -461,7 +516,13 @@ function(index) { $nbStudents, $trackingColumn, $trackingDirection, - $conditions + $conditions, + true, + false, + null, + (int) $sessionId, + $export_csv, + $user_ids ); $userRepo = Container::getUserRepository(); foreach ($usersTracking as $userTracking) { @@ -474,7 +535,7 @@ function(index) { $numberStudentsCompletedLP++; } $averageStudentTestScore = substr($userTracking[7], 0, -1); - $averageStudentsTestScore += $averageStudentTestScore; + $averageStudentsTestScore .= $averageStudentTestScore; if ('100' === $averageStudentTestScore) { $reducedAverage = 9; @@ -574,8 +635,8 @@ function(index) { $el->setSelected(7); $form->addElement('hidden', 'action', 'add'); $form->addElement('hidden', 'remindallinactives', 'true'); - $form->addElement('hidden', 'cidReq', $course->getCode()); - $form->addElement('hidden', 'id_session', api_get_session_id()); + $form->addElement('hidden', 'cid', api_get_course_int_id()); + $form->addElement('hidden', 'sid', api_get_session_id()); $form->addButtonSend(get_lang('Notify')); $extraFieldSelect = TrackingCourseLog::displayAdditionalProfileFields(); @@ -612,7 +673,7 @@ function(index) { $table->setDataFunctionParams($conditions); } - $parameters['cidReq'] = isset($_GET['cidReq']) ? Security::remove_XSS($_GET['cidReq']) : ''; + $parameters['cid'] = isset($_GET['cid']) ? Security::remove_XSS($_GET['cid']) : ''; $parameters['sid'] = $sessionId; $parameters['from'] = isset($_GET['myspace']) ? Security::remove_XSS($_GET['myspace']) : null; @@ -718,6 +779,11 @@ function(index) { $headers['first_login'] = get_lang('First access to course'); $table->set_header($headerCounter++, get_lang('Latest access in course'), false); $headers['latest_login'] = get_lang('Latest access in course'); + $table->set_header($headerCounter++, get_lang('Lp Finalization Date'), false); + $headers['lp_finalization_date'] = get_lang('Lp Finalization Date'); + $table->set_header($headerCounter++, get_lang('Quiz Finalization Date'), false); + $headers['quiz_finalization_date'] = get_lang('Quiz Finalization Date'); + $counter = $headerCounter; if ('true' === api_get_setting('show_email_addresses')) { $table->set_header($counter, get_lang('Email'), false); @@ -732,6 +798,15 @@ function(index) { $parameters['additional_profile_field'] = $fieldId; } } + if (isset($defaultExtraFields)) { + if (!empty($defaultExtraInfo)) { + foreach ($defaultExtraInfo as $field) { + $table->set_header($counter, $field['display_text'], false); + $headers[$field['variable']] = $field['display_text']; + $counter++; + } + } + } $table->set_header($counter, get_lang('Details'), false); $headers['Details'] = get_lang('Details'); @@ -742,7 +817,7 @@ function(index) { $parameters[$key] = $value; } } - $parameters['cidReq'] = $courseCode; + $parameters['cid'] = api_get_course_int_id(); $parameters['sid'] = $sessionId; $table->set_additional_parameters($parameters); // display buttons to un hide hidden columns @@ -783,15 +858,7 @@ function(index) { $groupTable->setHeaderContents(0, $column++, get_lang('Course progress')); $groupTable->setHeaderContents(0, $column++, get_lang('Exercise average')); -/*$exerciseList = ExerciseLib::get_all_exercises( - $courseInfo, - $sessionId, - false, - null, - false, - 3 -);*/ - +$exerciseList = []; $session = api_get_session_entity($sessionId); $qb = Container::getQuizRepository()->findAllByCourse($course, $session, null, 2, false); /** @var CQuiz[] $exercises */ @@ -946,7 +1013,8 @@ function(index) { $totalBestScoreAverageNotInLP = 0; $bestScoreAverageNotInLP = 0; if (!empty($exercises)) { - foreach ($exercises as $exerciseData) { + foreach ($exercises as $i => $exerciseData) { + $exerciseList[$i]['iid'] = $exerciseData->getIid(); foreach ($studentIdList as $userId) { $results = Event::get_best_exercise_results_by_user( $exerciseData->getIid(), @@ -975,6 +1043,13 @@ function(index) { 2 ).' %'; } + $bestScoreAverageNotInLP = (string) TrackingCourseLog::calcBestScoreAverageNotInLP( + $exerciseList, + $studentIdList, + (int) $courseInfo['real_id'], + $sessionId, + true + ); } $row = 1; @@ -1022,6 +1097,8 @@ function(index) { $csv_headers[] = get_lang('First access to course'); $csv_headers[] = get_lang('Latest access in course'); + $csv_headers[] = get_lang('Lp Finalization Date'); + $csv_headers[] = get_lang('Quiz Finalization Date'); if (isset($_GET['additional_profile_field'])) { foreach ($_GET['additional_profile_field'] as $fieldId) { @@ -1030,7 +1107,7 @@ function(index) { } ob_end_clean(); - $csvContentInSession = Session::read('csv_content'); + $csvContentInSession = Session::read('csv_content', []); // Adding headers before the content. array_unshift($csvContentInSession, $csv_headers); diff --git a/src/CoreBundle/Migrations/Schema/V200/Version20230913162700.php b/src/CoreBundle/Migrations/Schema/V200/Version20230913162700.php index 3e65d58aafa..d7b28e907cc 100644 --- a/src/CoreBundle/Migrations/Schema/V200/Version20230913162700.php +++ b/src/CoreBundle/Migrations/Schema/V200/Version20230913162700.php @@ -32,7 +32,7 @@ public function up(Schema $schema): void $resourceNodeRepo = $this->container->get(ResourceNodeRepository::class); $q = $this->entityManager->createQuery('SELECT c FROM Chamilo\CoreBundle\Entity\Course c'); - $updateConfigurations = [ + /*$updateConfigurations = [ ['table' => 'c_tool_intro', 'field' => 'intro_text'], ['table' => 'c_course_description', 'field' => 'content'], ['table' => 'c_quiz', 'fields' => ['description', 'text_when_finished']], @@ -48,7 +48,7 @@ public function up(Schema $schema): void ['table' => 'c_survey', 'fields' => ['title', 'subtitle']], ['table' => 'c_survey_question', 'fields' => ['survey_question', 'survey_question_comment']], ['table' => 'c_survey_question_option', 'field' => 'option_text'], - ]; + ];*/ /** @var Course $course */ foreach ($q->toIterable() as $course) { @@ -59,9 +59,9 @@ public function up(Schema $schema): void continue; } - foreach ($updateConfigurations as $config) { + /* foreach ($updateConfigurations as $config) { $this->updateContent($config, $courseDirectory, $courseId, $documentRepo); - } + }*/ $this->updateHtmlContent($courseDirectory, $courseId, $documentRepo, $resourceNodeRepo); } @@ -155,6 +155,13 @@ private function replaceOldURLsWithNew($itemDataText, $courseDirectory, $courseI $documentPath = str_replace('/courses/'.$courseDirectory.'/document/', '/', $videoPath); + error_log('Debugging Replace URLs:'); + error_log('Full URL: ' . $fullUrl); + error_log('Video Path: ' . $videoPath); + error_log('Actual Course Directory: ' . $actualCourseDirectory); + error_log('Processed Document Path: ' . $documentPath); + + /* $sql = "SELECT iid, path, resource_node_id FROM c_document WHERE c_id = $courseId AND path LIKE '$documentPath'"; $result = $this->connection->executeQuery($sql); $documents = $result->fetchAllAssociative(); @@ -170,7 +177,7 @@ private function replaceOldURLsWithNew($itemDataText, $courseDirectory, $courseI $contentText = str_replace($matches[0][$index], $replacement, $contentText); } } - } + }*/ } return $contentText; From bfb958a21b65344d8a3c88b99dfd372933a054cc Mon Sep 17 00:00:00 2001 From: christianbeeznst Date: Wed, 20 Nov 2024 16:23:40 -0500 Subject: [PATCH 04/11] Internal: add missing icons used in course log reporting - refs BT#22208 --- public/img/icons/32/attendance_list.png | Bin 0 -> 2029 bytes public/img/icons/32/scorms.png | Bin 0 -> 2310 bytes public/img/icons/32/scorms_na.png | Bin 0 -> 1223 bytes public/img/icons/32/security.png | Bin 0 -> 2419 bytes public/img/icons/32/security_na.png | Bin 0 -> 1068 bytes public/img/icons/32/tools.png | Bin 0 -> 1918 bytes public/img/icons/32/tools_na.png | Bin 0 -> 947 bytes public/img/icons/32/user.png | Bin 0 -> 1969 bytes public/img/icons/32/user_na.png | Bin 0 -> 1084 bytes 9 files changed, 0 insertions(+), 0 deletions(-) create mode 100755 public/img/icons/32/attendance_list.png create mode 100755 public/img/icons/32/scorms.png create mode 100755 public/img/icons/32/scorms_na.png create mode 100755 public/img/icons/32/security.png create mode 100644 public/img/icons/32/security_na.png create mode 100755 public/img/icons/32/tools.png create mode 100755 public/img/icons/32/tools_na.png create mode 100755 public/img/icons/32/user.png create mode 100755 public/img/icons/32/user_na.png diff --git a/public/img/icons/32/attendance_list.png b/public/img/icons/32/attendance_list.png new file mode 100755 index 0000000000000000000000000000000000000000..98bafe29b24539df491942fde0205acafbe8788e GIT binary patch literal 2029 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 literal 0 HcmV?d00001 diff --git a/public/img/icons/32/scorms_na.png b/public/img/icons/32/scorms_na.png new file mode 100755 index 0000000000000000000000000000000000000000..38e90a12ed7d87bd76e432ec67b06547929d156a GIT binary patch literal 1223 zcmV;&1UUPNP)kdg00001b5ch_0Itp) z=>Px#0%A)?L;(MXkIcUS000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2ipn|6(1=( zf&Lo+00c`(L_t(Y$EB5BY?Df;I?4*&_qugmzuw~or7*^a z3Fq9LHPNQ0!=41z}zFWs@t;2vIVq7*KgYE)Y4%G$b*G8KW7GE3;GB^C}p9 z-kMF^CB#2isH6fL3JE{}5*3XFCYi@VJzMZIG8$~dnkO)nd(;|=sUpqKcHs zIa}Nx4RebkjF>3KhMiJsSRle>K4S~6$HhjLnh%KYG^qS!+u_&Tetw{hS6EFUM$DKf z#!3lBD%qC{;)9`cgAN9TXu_@u$ID->aW68(yIkaN1m)CHf`tN1SP)b$gAdV87Z+LR zO9xZO$Y{2$6%{o^Iggo2{7e%fh)OkH99UUkJ_jA<3~dZBlSp(YkH}6UG7Xw}bBWoG zMj1=Y5Wz?t0VWACL=hfV6DLfNKe$Gct$fXqP}e~@n6E~CAteX4HE?vQcQ+8Q^fd|X6ET-nmQV=K8QuN z_<)}ts$mSAYTQ;qAsT8f({&(OIL&C(;^l2dxkN8_=;L>0@lsDI#ki@(k2CkG1iH$Qd}ykR<59AtFkrxskNphO zPnac2XkZOK%9iD$fs9+&navZ#dwSS~N)iPXftf}E=!|iV_b{xqT&zv%hkTzW(0eZb z_RZc+S~G4062bk1N`tk`zM99D&q1pl(0a>V2cBaU+ea1|%kA&OGkd z;7z(YP2Wu9>RgB3#V~S(tqZ%d!L`3`ce8gr4U}`6MOw%b<_u@KvN(2as#A8!J~{Qw z#v%%p9p!u0zW!3p2BxVX$oW*be21002ovPDHLkV1fv8Bz^z@ literal 0 HcmV?d00001 diff --git a/public/img/icons/32/security.png b/public/img/icons/32/security.png new file mode 100755 index 0000000000000000000000000000000000000000..80e060dbf674ab10ae5ad1e3e4e0441d9592f3cd GIT binary patch literal 2419 zcmW+&YgAH+7CvwYG>?c^Q^-uz(n=GMDJ`vojafES_O`|n6||Zhw9GU~Koj3d%>%3q zZg*B!8z-Hd8LexA;v=(?nsLM|5f!zUk}pV+bGi4&-rw4P_FDT}-`@KxjM}rqe(Cb1 z007uW?hM~=Me_f`25ZgdE?%6m0wyVJcNhTFJPuk?^wwIF(zZvZ9pEOWU5Go&0m9ha zcn&!-IWCE_pA*Nr-faNJT07I}V zV2xA=*3COe11$7FDmC{f1n6UI^>YJNT0UbVflkj&qtVTuziU^vk9Ha|`0Pxql*WAh7cn@voOI&RVw*v~0JBP{fwgCA0~>j!t2&dY zSaB;>Q!my1`OZfKmw7mz1sH&1y0XmUE_211^G`;L_WiLB%N&}@ffRgmoXPyrL6y%% zL;f=qfWn+thL0RM}pMCEB7EgyfZ zc#H>Z$U6gA@p%vOg}UCo`@T&Ta_-oUo^5%uO&LCSLpRm|L$D${e(+&qnNas97u%_o z5l8s1S!$<89m<)qJl)?x$)Yjdb?A&g6OvuV5Wnqu6-Zq)<4S%+pxej1pqsZ7r!MR* zSZ_%mat~6C(d`5-&F&}y!#^d~J4eA7h}?bUK#@$>Q_O|9d<$;=TAr?>Lk9iYE1_dd zp-F?TS*_%%XG&$2?Lmi<0fl5wpt%8jy2gV<{84tE6TI%-W2I_oni7_8QHm4v9FABm z;ba;#eu%cT4kw&@BRq;DFrsi~VUz+nWmr?_le)Gzurc#61OOKX>5 zS9EbRHTOGVn6tD&0rVh)z^i$Qux~U7talUhg+krT_AR1r{!yaT!rR_D-K~I1Vwli} z)9DYjG7=_`w^{2@=S&tz#s9h`EU$`IMjamNui--;3QK$jE^VQ> z|JzH|)vpk&wXd|~hJvaDxxVasfu|>`6@t3C=jbuRVvcF{V~Dy%!TQS6L{TNl^Rv-k zo%31ddH{=B1jHA@4ucrNs-?toBC$_%y8KY9riq7Ihw*8bvC8>7Pl4k%_#YT581*|k zd6zNU+F`d_| z5lSE)@`~#EwMQVIAAcVrvg;RcT5yS2S|=ZBoMJQwG1iv}o00ND-J67s^6R}yjO|mD zKcB3pN4)5;d43(5q~uOH!q$;@ZA(103EL+Ep*!n}1_u`%+DgA@sR)gwir9qPCwqiBp&s2LYKTUfa8X<2T zE|w9mEIUs(BOz{N0ol_q+2$>(-{C@S6rW*>bpp)VO0OYT?8|ECW{HA$xvwek}l6C_{FT0g#)5C5* zc}_bNczoN0{>>b$9__VqzP>}xX z_4Hm+gJ7$EkcB(!dJVeyx>1=9_=3JToH#LQ5ZIhkT3RZss3;W{7LufDp}x&H32C?T zu|7LDVkNE=(n5QTFeV(^E8rwBYB>4N<6fs%_wa z5#^OO;F@IHUA0uR*$8Q3Zhk7%n=HS;{KZi8Z+ag>Y|f#bR%RnFORIiZ68(!`dHL7f zQ*%@IB@)S!ub1wU`1|`aJnFu@g#Txp)V9)=?LwhYR2^j=q~{eO-L)w%t~n&_@#vpl zFlrw#&h15gI>Ign&^8iKzDX2$V*8=rUkp4C*dH5PwwuX>q@&TqXu)~d&pFqBbZ`WS zu=5SxVg@&&C*J(0qc)*8Ky*AL{f*+n(IUHTw`+4O1neLYt@(3oYM zz*C*?q1!0nr!UEtf^L=o@cJ9|7>_!-4$~ScN0W~IyqG_42>GkUL6hU=X8m;S!U=Z} gqZSzLQD_Kwzx$4jArZyPJ;zh+isGXMYp literal 0 HcmV?d00001 diff --git a/public/img/icons/32/security_na.png b/public/img/icons/32/security_na.png new file mode 100644 index 0000000000000000000000000000000000000000..3a63ede9101d5c88ad59e48a781a7c1412383ed9 GIT binary patch literal 1068 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 Ef;~oOBLDyZ literal 0 HcmV?d00001 diff --git a/public/img/icons/32/tools_na.png b/public/img/icons/32/tools_na.png new file mode 100755 index 0000000000000000000000000000000000000000..ee95e934f704b32a954f9833d95bd958578e4713 GIT binary patch literal 947 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 Date: Fri, 29 Nov 2024 19:22:16 -0500 Subject: [PATCH 05/11] Internal: create login record after inscription of new user --- public/main/auth/inscription.php | 8 ++++++-- src/CoreBundle/Framework/Container.php | 6 ++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/public/main/auth/inscription.php b/public/main/auth/inscription.php index 15c713b32ad..b97a55ff638 100644 --- a/public/main/auth/inscription.php +++ b/public/main/auth/inscription.php @@ -2,6 +2,7 @@ /* For licensing terms, see /license.txt */ use Chamilo\CoreBundle\Entity\User; +use Chamilo\CoreBundle\Framework\Container; use Chamilo\CoreBundle\ServiceHelper\ContainerHelper; use ChamiloSession as Session; use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; @@ -1159,6 +1160,7 @@ $token = new UsernamePasswordToken($userEntity, $providerKey, $roles); $container->get(ContainerHelper::class)->getTokenStorage()->setToken($token); + $request = $container->get('request_stack')->getMainRequest(); $sessionHandler = $container->get('request_stack')->getSession(); $sessionHandler->set('_security_' . $providerKey, serialize($token)); $userData = [ @@ -1174,9 +1176,11 @@ $is_allowedCreateCourse = isset($values['status']) && 1 == $values['status']; $sessionHandler->set('is_allowedCreateCourse', $is_allowedCreateCourse); - // Stats - //Event::eventLogin($user_id); + Container::getTrackELoginRepository() + ->createLoginRecord($userEntity, new DateTime(), $request->getClientIp()) + ; + // @todo implement Auto-subscribe according to STATUS_autosubscribe setting // last user login date is now $user_last_login_datetime = 0; // used as a unix timestamp it will correspond to : 1 1 1970 diff --git a/src/CoreBundle/Framework/Container.php b/src/CoreBundle/Framework/Container.php index 821d7472aaf..a15e42fe2e0 100644 --- a/src/CoreBundle/Framework/Container.php +++ b/src/CoreBundle/Framework/Container.php @@ -39,6 +39,7 @@ use Chamilo\CoreBundle\Repository\TrackEDownloadsRepository; use Chamilo\CoreBundle\Repository\TrackEExerciseRepository; use Chamilo\CoreBundle\Repository\TrackELoginRecordRepository; +use Chamilo\CoreBundle\Repository\TrackELoginRepository; use Chamilo\CoreBundle\Serializer\UserToJsonNormalizer; use Chamilo\CoreBundle\ServiceHelper\ContainerHelper; use Chamilo\CoreBundle\ServiceHelper\ThemeHelper; @@ -641,6 +642,11 @@ public static function getSocialPostRepository(): SocialPostRepository return self::$container->get(SocialPostRepository::class); } + public static function getTrackELoginRepository(): TrackELoginRepository + { + return self::$container->get(TrackELoginRepository::class); + } + public static function getTrackELoginRecordRepository(): TrackELoginRecordRepository { return self::$container->get(TrackELoginRecordRepository::class); From 9b1f1e6b69ce98fc2721affd5117d52f6c081568 Mon Sep 17 00:00:00 2001 From: Christian Beeznest Date: Tue, 3 Dec 2024 00:59:10 -0500 Subject: [PATCH 06/11] Internal: Fix CSV export formatting and score calculation issues - refs BT#22096 --- public/main/inc/lib/export.lib.inc.php | 45 +++- public/main/inc/lib/sessionmanager.lib.php | 290 +++++++++------------ public/main/inc/lib/tracking.lib.php | 19 -- public/main/my_space/myStudents.php | 2 +- 4 files changed, 157 insertions(+), 199 deletions(-) diff --git a/public/main/inc/lib/export.lib.inc.php b/public/main/inc/lib/export.lib.inc.php index 07178e8ba8f..3396dede15b 100644 --- a/public/main/inc/lib/export.lib.inc.php +++ b/public/main/inc/lib/export.lib.inc.php @@ -26,14 +26,9 @@ public function __construct() /** * Export tabular data to CSV-file. * - * @param array $data - * @param string $filename - * @param bool $writeOnly Whether to only write on disk or also send for download - * @param string $enclosure - * * @return mixed csv raw data | false if no data to export | string file path if success in $writeOnly mode */ - public static function arrayToCsv($data, $filename = 'export', $writeOnly = false, $enclosure = '"') + public static function arrayToCsv(array $data, string $filename = 'export', bool $writeOnly = false, string $enclosure = '"') { if (empty($data)) { return false; @@ -55,6 +50,44 @@ public static function arrayToCsv($data, $filename = 'export', $writeOnly = fals return $filePath; } + /** + * Converts an array of data into a CSV file and optionally sends it for download. + * + * @return string|void Returns the file path if $writeOnly is true, otherwise sends the file for download and exits. + */ + public static function arrayToCsvSimple(array $data, string $filename = 'export', bool $writeOnly = false) + { + $file = api_get_path(SYS_ARCHIVE_PATH) . uniqid('') . '.csv'; + + $handle = fopen($file, 'w'); + + if ($handle === false) { + throw new \RuntimeException("Unable to create or open the file: $file"); + } + + if (is_array($data)) { + foreach ($data as $row) { + $line = ''; + if (is_array($row)) { + foreach ($row as $value) { + $line .= '"' . str_replace('"', '""', (string)$value) . '";'; + } + } + fwrite($handle, rtrim($line, ';') . "\n"); + } + } + + fclose($handle); + + if (!$writeOnly) { + DocumentManager::file_send_for_download($file, true, $filename . '.csv'); + unlink($file); + exit; + } + + return $file; + } + /** * Export tabular data to XLS-file. */ diff --git a/public/main/inc/lib/sessionmanager.lib.php b/public/main/inc/lib/sessionmanager.lib.php index 9359acf2b65..851c384018a 100644 --- a/public/main/inc/lib/sessionmanager.lib.php +++ b/public/main/inc/lib/sessionmanager.lib.php @@ -10232,38 +10232,9 @@ public static function getAllUserIdsInSession(int $sessionId): array */ public static function exportSessionsAsCSV(array $selectedSessions): void { - $csvHeaders = []; - $csvHeaders[] = get_lang('Session name'); - $csvHeaders[] = get_lang('Session start date'); - $csvHeaders[] = get_lang('Session end date'); - $csvHeaders[] = get_lang('Course name'); - $csvHeaders[] = get_lang('Official code'); - - if (api_sort_by_first_name()) { - $csvHeaders[] = get_lang('First name'); - $csvHeaders[] = get_lang('Last name'); - } else { - $csvHeaders[] = get_lang('Last name'); - $csvHeaders[] = get_lang('First name'); - } - - $csvHeaders[] = get_lang('Login'); - $csvHeaders[] = get_lang('Training time'); - $csvHeaders[] = get_lang('Course progress'); - $csvHeaders[] = get_lang('Exercise progress'); - $csvHeaders[] = get_lang('Exercise average'); - $csvHeaders[] = get_lang('Score'); - $csvHeaders[] = get_lang('Score').' - '.get_lang('Best attempt'); - $csvHeaders[] = get_lang('Student_publication'); - $csvHeaders[] = get_lang('Messages'); - $csvHeaders[] = get_lang('Classes'); - $csvHeaders[] = get_lang('Registration date'); - $csvHeaders[] = get_lang('FirstLogin in course'); - $csvHeaders[] = get_lang('Latest login in course'); - $csvHeaders[] = get_lang('Lp finalization date'); - $csvHeaders[] = get_lang('Quiz finalization date'); - $csvData = []; + $headersGenerated = false; + $csvHeaders = []; foreach ($selectedSessions as $sessionId) { $courses = SessionManager::get_course_list_by_session_id($sessionId); @@ -10271,30 +10242,24 @@ public static function exportSessionsAsCSV(array $selectedSessions): void if (!empty($courses)) { foreach ($courses as $course) { $courseCode = $course['course_code']; + $courseId = $course['id']; $studentList = CourseManager::get_student_list_from_course_code( $courseCode, true, $sessionId ); - $nbStudents = count($studentList); $userIds = array_keys($studentList); - $csvContentInSession = TrackingCourseLog::getUserData( - null, - $nbStudents, - null, - null, - [], - false, - true, - $courseCode, - $sessionId, - true, - $userIds - ); - if (!empty($csvContentInSession)) { - $csvData = array_merge($csvData, $csvContentInSession); + [$generatedHeaders, $csvContent] = self::generateSessionCourseReportData($sessionId, $courseId, $userIds); + + if (!$headersGenerated) { + $csvHeaders = $generatedHeaders; + $headersGenerated = true; + } + + foreach ($csvContent as $row) { + $csvData[] = $row; } } } @@ -10302,8 +10267,8 @@ public static function exportSessionsAsCSV(array $selectedSessions): void if (!empty($csvData)) { array_unshift($csvData, $csvHeaders); - $filename = 'export_session_courses_reports_complete_' . api_get_local_time(); - Export::arrayToCsv($csvData, $filename); + $filename = 'export_session_courses_reports_complete_' . date('Y-m-d_H-i-s') . '.csv'; + Export::arrayToCsvSimple($csvData, $filename); exit; } } @@ -10313,161 +10278,140 @@ public static function exportSessionsAsCSV(array $selectedSessions): void */ public static function exportSessionsAsZip(array $sessionList): void { - // Create a temporary ZIP file $tempZipFile = api_get_path(SYS_ARCHIVE_PATH) . api_get_unique_id() . '.zip'; $tempDir = dirname($tempZipFile); - // Check if the directory exists and has write permissions if (!is_dir($tempDir) || !is_writable($tempDir)) { exit("The directory for creating the ZIP file does not exist or lacks write permissions: $tempDir"); } - // Create a new ZIP archive $zip = new \ZipArchive(); - - // Try to open the ZIP file for writing if ($zip->open($tempZipFile, \ZipArchive::CREATE | \ZipArchive::OVERWRITE) !== true) { exit("Unable to open the ZIP file for writing: $tempZipFile"); } - $csvList = []; - - // Process each session in the list foreach ($sessionList as $sessionItemId) { - $em = Database::getManager(); - $sessionRepository = $em->getRepository(Session::class); - $session = $sessionRepository->find($sessionItemId); + $courses = SessionManager::get_course_list_by_session_id($sessionItemId); - if ($session->getNbrCourses() > 0) { - $courses = $session->getCourses(); - $courseList = []; - - // Collect courses for the session - foreach ($courses as $sessionRelCourse) { - $courseList[] = $sessionRelCourse->getCourse(); - } - - foreach ($courseList as $course) { - $courseId = $course->getId(); - $courseInfo = api_get_course_info_by_id($courseId); - $addExerciseOption = api_get_configuration_value('add_exercise_best_attempt_in_report'); - $sortByFirstName = api_sort_by_first_name(); - $bestScoreLabel = get_lang('Score') . ' - ' . get_lang('Best attempt'); - $courseCode = $courseInfo['code']; - - // Prepare CSV headers - $csvHeaders = []; - $csvHeaders[] = get_lang('Official code'); - - if ($sortByFirstName) { - $csvHeaders[] = get_lang('First name'); - $csvHeaders[] = get_lang('Last name'); - } else { - $csvHeaders[] = get_lang('Last name'); - $csvHeaders[] = get_lang('First name'); - } - - $csvHeaders[] = get_lang('Login'); - $csvHeaders[] = get_lang('Training time'); - $csvHeaders[] = get_lang('Course progress'); - $csvHeaders[] = get_lang('Exercise progress'); - $csvHeaders[] = get_lang('Exercise average'); - $csvHeaders[] = get_lang('Score'); - $csvHeaders[] = $bestScoreLabel; - - // Include exercise results if available - $exerciseResultHeaders = []; - if (!empty($addExerciseOption) && isset($addExerciseOption['courses']) && - isset($addExerciseOption['courses'][$courseCode])) { - foreach ($addExerciseOption['courses'][$courseCode] as $exerciseId) { - $exercise = new Exercise(); - $exercise->read($exerciseId); - if ($exercise->iid) { - $title = get_lang('Exercise') . ': ' . $exercise->get_formated_title(); - $csvHeaders[] = $title; - $exerciseResultHeaders[] = $title; - } - } - } - - // Add more fields to the CSV headers - $csvHeaders[] = get_lang('Student_publication'); - $csvHeaders[] = get_lang('Messages'); - $csvHeaders[] = get_lang('Classes'); - $csvHeaders[] = get_lang('Registration date'); - $csvHeaders[] = get_lang('First login in course'); - $csvHeaders[] = get_lang('Latest login in course'); - - // Get the list of students for the course + if (!empty($courses)) { + foreach ($courses as $course) { + $courseCode = $course['course_code']; + $courseId = $course['id']; $studentList = CourseManager::get_student_list_from_course_code($courseCode, true, $sessionItemId); - $nbStudents = count($studentList); - - // Pass the necessary data as parameters instead of using globals $userIds = array_keys($studentList); - // Get the user data for CSV content - $csvContentInSession = TrackingCourseLog::getUserData( - null, - $nbStudents, - null, - null, - [], - true, - true, - $courseCode, - $sessionItemId, - true, - $userIds - ); - array_unshift($csvContentInSession, $csvHeaders); + [$csvHeaders, $csvContent] = self::generateSessionCourseReportData($sessionItemId, $courseId, $userIds); + array_unshift($csvContent, $csvHeaders); - // Get session info and dates $sessionInfo = api_get_session_info($sessionItemId); - $sessionDates = SessionManager::parseSessionDates($session); - - // Add session name and dates to the CSV content - array_unshift($csvContentInSession, [get_lang('Date'), $sessionDates['access']]); - array_unshift($csvContentInSession, [get_lang('SessionName'), Security::remove_XSS($sessionInfo['name'])]); - - // Prepare CSV file information - $csvList[] = [ - 'session_id' => $sessionItemId, - 'session_name' => $session->getTitle(), - 'course_id' => $courseId, - 'course_name' => $courseInfo['name'], - 'path' => Export::arrayToCsv($csvContentInSession, '', true), // Generate the CSV - ]; - } - } - } + $courseInfo = api_get_course_info_by_id($courseId); + $csvFileName = $sessionInfo['name'] . '_' . $courseInfo['name'] . '.csv'; - // Add the generated CSV files to the ZIP archive - foreach ($csvList as $csv) { - $newFileName = $csv['session_id'] . '_' . $csv['session_name'] . '-' . $csv['course_id'] . '_' . $csv['course_name'] . '.csv'; + $csvFilePath = Export::arrayToCsvSimple($csvContent, $csvFileName, true); - if (file_exists($csv['path'])) { - $zip->addFile($csv['path'], $newFileName); + if ($csvFilePath && file_exists($csvFilePath)) { + $zip->addFile($csvFilePath, $csvFileName); + } + } } } - // Close the ZIP file - if ($zip->close() === false) { + if (!$zip->close()) { exit("Could not close the ZIP file correctly."); } - // Clean up the CSV files after adding them to the ZIP - foreach ($csvList as $csv) { - if (file_exists($csv['path'])) { - unlink($csv['path']); - } - } - - // Send the ZIP file for download if (file_exists($tempZipFile)) { DocumentManager::file_send_for_download($tempZipFile, true); - unlink($tempZipFile); // Delete the temporary ZIP file after download + unlink($tempZipFile); } else { exit("The ZIP file was not created correctly."); } } + + private static function generateSessionCourseReportData($sessionId, $courseId, $userIds): array + { + $em = Database::getManager(); + $sessionRepository = $em->getRepository(Session::class); + $session = $sessionRepository->find($sessionId); + + if (!$session instanceof Session) { + throw new \InvalidArgumentException("Invalid session object for session ID $sessionId"); + } + + $courseInfo = api_get_course_info_by_id($courseId); + $courseCode = $courseInfo['code']; + + $csvHeaders = [ + get_lang('Session name'), + get_lang('Session access dates'), + get_lang('Session display dates'), + get_lang('Course name'), + get_lang('Official code'), + get_lang('First name'), + get_lang('Last name'), + get_lang('Login'), + get_lang('Training time'), + get_lang('Course progress'), + get_lang('Exercise progress'), + get_lang('Exercise average'), + get_lang('Score'), + get_lang('Score') . ' - ' . get_lang('Best attempt'), + get_lang('Student_publication'), + get_lang('Messages'), + get_lang('Classes'), + get_lang('Registration date'), + get_lang('First login in course'), + get_lang('Latest login in course'), + ]; + + $csvData = TrackingCourseLog::getUserData( + null, + count($userIds), + null, + null, + [], + true, + true, + $courseCode, + $sessionId, + true, + $userIds + ); + + $rawCsvContent = ChamiloSession::read('csv_content'); + + if (empty($rawCsvContent)) { + throw new \RuntimeException("No CSV content found in session for course $courseCode and session $sessionId."); + } + + $csvContent = []; + foreach ($rawCsvContent as $row) { + $alignedRow = [ + $row['session_name'] ?? '', + $row['session_startdate'] ?? '', + $row['session_enddate'] ?? '', + $row['course_name'] ?? '', + $row['official_code'] ?? '', + $row['firstname'] ?? '', + $row['lastname'] ?? '', + $row['username'] ?? '', + $row['time'] ?? '', + $row['average_progress'] ?? '', + $row['exercise_progress'] ?? '', + $row['exercise_average'] ?? '', + $row['student_score'] ?? '', + $row['student_score_best'] ?? '', + $row['count_assignments'] ?? '', + $row['count_messages'] ?? '', + $row['classes'] ?? '', + $row['registered_at'] ?? '', + $row['first_connection'] ?? '', + $row['last_connection'] ?? '', + ]; + $csvContent[] = $alignedRow; + } + + return [$csvHeaders, $csvContent]; + } + } diff --git a/public/main/inc/lib/tracking.lib.php b/public/main/inc/lib/tracking.lib.php index b4daec58eb9..763af042d25 100644 --- a/public/main/inc/lib/tracking.lib.php +++ b/public/main/inc/lib/tracking.lib.php @@ -2899,25 +2899,6 @@ public static function get_avg_student_score( } $sessionCondition = api_get_session_condition($sessionId); - //$sessionId = (int) $sessionId; - /*if (count($lp_ids) > 0) { - $condition_session = " AND session_id = $sessionId "; - } else { - $condition_session = " WHERE session_id = $sessionId "; - } - - // Check the real number of LPs corresponding to the filter in the - // database (and if no list was given, get them all) - if (empty($sessionId)) { - $sql = "SELECT DISTINCT(iid), use_max_score - FROM $lp_table - WHERE - c_id = $courseId AND - (session_id = 0 OR session_id IS NULL) $condition_lp "; - } else { - - }*/ - $lp_list = $use_max_score = []; if (empty($condition_lp)) { $repo = Container::getLpRepository(); diff --git a/public/main/my_space/myStudents.php b/public/main/my_space/myStudents.php index cffe3e1da37..69fca2ae0eb 100644 --- a/public/main/my_space/myStudents.php +++ b/public/main/my_space/myStudents.php @@ -350,7 +350,7 @@ $studentId, $course, [], - $sId, + api_get_session_entity($sId), false, false, true From 62b1efdd632cac24b9c4202c264d55187c9e56e7 Mon Sep 17 00:00:00 2001 From: NicoDucou Date: Tue, 3 Dec 2024 09:13:29 +0100 Subject: [PATCH 07/11] Language: Minor: Fix Administration Statistiques translation in french - refs BT#22247 --- translations/messages.fr.po | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/translations/messages.fr.po b/translations/messages.fr.po index b3309760e08..582d877c68c 100644 --- a/translations/messages.fr.po +++ b/translations/messages.fr.po @@ -1812,7 +1812,7 @@ msgid "Course program" msgstr "Cahier des charges" msgid "Statistics" -msgstr "Suivi" +msgstr "Statistiques" msgid "Upload page and link to Home Page" msgstr "Déposer page et lier à l'accueil" From 1b2f7651e3e8374912b82ffbe3a1dffe81a6733b Mon Sep 17 00:00:00 2001 From: NicoDucou Date: Tue, 3 Dec 2024 14:13:45 +0100 Subject: [PATCH 08/11] Language: Adding translation variables and translation to EN, ES and FR + fixing variable name - refs BT#22247 --- assets/locales/en.json | 4 ++-- public/main/course_info/infocours.php | 2 +- translations/messages.en.po | 21 +++++++++++++++++++++ translations/messages.es.po | 21 +++++++++++++++++++++ translations/messages.fr.po | 21 +++++++++++++++++++++ translations/messages.pot | 21 +++++++++++++++++++++ 6 files changed, 87 insertions(+), 3 deletions(-) diff --git a/assets/locales/en.json b/assets/locales/en.json index e8143f33f92..78055a9e8a8 100644 --- a/assets/locales/en.json +++ b/assets/locales/en.json @@ -18,7 +18,7 @@ "Surveys": "Surveys", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", + "Course settings": "Course settings", "Sign in": "Sign in", "My courses": "My courses", "Start recording": "Start recording", @@ -578,4 +578,4 @@ "Select the destination folder": "Select the destination folder", "Select a folder": "Select a folder", "Move document": "Move document" -} \ No newline at end of file +} diff --git a/public/main/course_info/infocours.php b/public/main/course_info/infocours.php index e5ccbe46fbf..3efd1f27d03 100644 --- a/public/main/course_info/infocours.php +++ b/public/main/course_info/infocours.php @@ -311,7 +311,7 @@ 'radio', 'email_alert_to_teacher_on_new_user_in_course', null, - get_lang('To teachar and tutor'), + get_lang('To teacher and tutor'), 2 ); $group[] = $form->createElement( diff --git a/translations/messages.en.po b/translations/messages.en.po index ccb12f958e2..f9d047b6ff2 100644 --- a/translations/messages.en.po +++ b/translations/messages.en.po @@ -27805,3 +27805,24 @@ msgstr "Select the destination folder" msgid "All users" msgstr "All users" + +msgid "Edit introduction" +msgstr "Edit introduction" + +msgid "To teacher and tutor" +msgstr "To teacher and tutor" + +msgid "To HR only" +msgstr "To HR only" + +msgid "Only for teachers" +msgstr "Only for teachers" + +msgid "Only for students" +msgstr "Only for students" + +msgid "Autolaunch settings" +msgstr "Autolaunch settings" + +msgid "Auto-launch" +msgstr "Auto-launch" diff --git a/translations/messages.es.po b/translations/messages.es.po index da90b620ff5..db2e6946b17 100644 --- a/translations/messages.es.po +++ b/translations/messages.es.po @@ -28783,3 +28783,24 @@ msgstr "Seleccione la carpeta de destino" msgid "All users" msgstr "Todos los usuarios" + +msgid "Edit introduction" +msgstr "Editar la introduccion" + +msgid "To teacher and tutor" +msgstr "Para profesor y tutor" + +msgid "To HR only" +msgstr "Solo para RRHH" + +msgid "Only for teachers" +msgstr "Solo para profesores" + +msgid "Only for students" +msgstr "Solo para estudiantes" + +msgid "Autolaunch settings" +msgstr "Configuración de inicio automático" + +msgid "Auto-launch" +msgstr "Inicio automático" diff --git a/translations/messages.fr.po b/translations/messages.fr.po index 582d877c68c..b6b9d378a41 100644 --- a/translations/messages.fr.po +++ b/translations/messages.fr.po @@ -29585,3 +29585,24 @@ msgstr "Choisir le dossier de destination" msgid "All users" msgstr "Tous les utilisateurs" + +msgid "Edit introduction" +msgstr "Éditer l'introduction" + +msgid "To teacher and tutor" +msgstr "Pour le professeur et le tuteur" + +msgid "To HR only" +msgstr "Pour les RH uniquement" + +msgid "Only for teachers" +msgstr "Pour les professeurs uniquement" + +msgid "Only for students" +msgstr "Pour les apprenants uniquement" + +msgid "Autolaunch settings" +msgstr "Paramètres d'auto démarrage" + +msgid "Auto-launch" +msgstr "Auto-démarrage" diff --git a/translations/messages.pot b/translations/messages.pot index 1d55378b7c8..11efd85d20d 100644 --- a/translations/messages.pot +++ b/translations/messages.pot @@ -27791,3 +27791,24 @@ msgstr "" msgid "All users" msgstr "" + +msgid "Edit introduction" +msgstr "" + +msgid "To teacher and tutor" +msgstr "" + +msgid "To HR only" +msgstr "" + +msgid "Only for teachers" +msgstr "" + +msgid "Only for students" +msgstr "" + +msgid "Autolaunch settings" +msgstr "" + +msgid "Auto-launch" +msgstr "" From 88c7bb91b0f70b2c0fc612b99c69d150d98245fc Mon Sep 17 00:00:00 2001 From: NicoDucou Date: Tue, 3 Dec 2024 14:15:08 +0100 Subject: [PATCH 09/11] Language: Adding json update - refs BT#22247 --- assets/locales/ar.json | 2 +- assets/locales/ast_ES.json | 2 +- assets/locales/bg.json | 2 +- assets/locales/bn_BD.json | 1 - assets/locales/bo_CN.json | 1 - assets/locales/bs_BA.json | 1 - assets/locales/ca_ES.json | 2 +- assets/locales/cs_CZ.json | 1 - assets/locales/da.json | 1 - assets/locales/de.json | 2 +- assets/locales/el.json | 2 +- assets/locales/en.json | 2 +- assets/locales/eo.json | 1 - assets/locales/es.json | 4 ++-- assets/locales/eu_ES.json | 2 +- assets/locales/fa_AF.json | 1 - assets/locales/fa_IR.json | 2 +- assets/locales/fi_FI.json | 2 +- assets/locales/fo_FO.json | 1 - assets/locales/fr_FR.json | 4 ++-- assets/locales/fur.json | 2 +- assets/locales/gl.json | 2 +- assets/locales/he_IL.json | 2 +- assets/locales/hi.json | 1 - assets/locales/hr_HR.json | 1 - assets/locales/hu_HU.json | 1 - assets/locales/id_ID.json | 1 - assets/locales/it.json | 2 +- assets/locales/ja.json | 2 +- assets/locales/ka_GE.json | 1 - assets/locales/ko_KR.json | 1 - assets/locales/lt_LT.json | 1 - assets/locales/lv_LV.json | 2 +- assets/locales/mk_MK.json | 1 - assets/locales/ms_MY.json | 1 - assets/locales/nl.json | 2 +- assets/locales/nn_NO.json | 1 - assets/locales/oc.json | 2 +- assets/locales/pl_PL.json | 2 +- assets/locales/ps.json | 1 - assets/locales/pt_PT.json | 2 +- assets/locales/qu_PE.json | 2 +- assets/locales/ro_RO.json | 2 +- assets/locales/ru_RU.json | 1 - assets/locales/sk_SK.json | 2 +- assets/locales/sl_SI.json | 2 +- assets/locales/so_SO.json | 1 - assets/locales/sr_RS.json | 2 +- assets/locales/sv_SE.json | 2 +- assets/locales/sw_KE.json | 1 - assets/locales/th.json | 1 - assets/locales/tl_PH.json | 2 +- assets/locales/tr.json | 1 - assets/locales/uk_UA.json | 1 - assets/locales/vi_VN.json | 1 - assets/locales/xh_ZA.json | 1 - assets/locales/yo_NG.json | 1 - assets/locales/zh_CN.json | 2 +- assets/locales/zh_TW.json | 1 - 59 files changed, 32 insertions(+), 61 deletions(-) diff --git a/assets/locales/ar.json b/assets/locales/ar.json index 8be93a18757..4ff4a10ed1c 100644 --- a/assets/locales/ar.json +++ b/assets/locales/ar.json @@ -18,7 +18,7 @@ "Surveys": "\u0627\u0644\u0627\u0633\u062a\u0628\u064a\u0627\u0646\u0627\u062a", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", + "Course settings": "\u0627\u0639\u062f\u0627\u062f\u0627\u062a \u0627\u0644\u0645\u0642\u0631\u0631", "Sign in": "\u062a\u0633\u062c\u064a\u0644 \u0627\u0644\u062f\u062e\u0648\u0644", "My courses": "\u0645\u0642\u0631\u0631\u0627\u062a\u064a", "Start recording": "\u0628\u062f\u0623 \u0627\u0644\u062a\u0633\u062c\u064a\u0644", diff --git a/assets/locales/ast_ES.json b/assets/locales/ast_ES.json index e4a68ee7fc7..31d1ac08e93 100644 --- a/assets/locales/ast_ES.json +++ b/assets/locales/ast_ES.json @@ -14,7 +14,7 @@ "Survey": "Encuesta", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", + "Course settings": "Configuraci\u00f3n de la lleici\u00f3n", "My courses": "Los mios cursos", "Attach": "Attach", "Home": "P\u00e1xina d'entamu", diff --git a/assets/locales/bg.json b/assets/locales/bg.json index 32dd5bceed4..df3a6125550 100644 --- a/assets/locales/bg.json +++ b/assets/locales/bg.json @@ -18,7 +18,7 @@ "Surveys": "\u0410\u043d\u043a\u0435\u0442\u0438", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", + "Course settings": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438", "My courses": "\u041c\u043e\u0438\u0442\u0435 \u043a\u0443\u0440\u0441\u043e\u0432\u0435", "Attach": "Attach", "Home": "\u041d\u0430\u0447\u0430\u043b\u043e", diff --git a/assets/locales/bn_BD.json b/assets/locales/bn_BD.json index 31f3756a609..7b2f9b317eb 100644 --- a/assets/locales/bn_BD.json +++ b/assets/locales/bn_BD.json @@ -1,7 +1,6 @@ { "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", "Attach": "Attach", "Is it editable by the invitees?": "Is it editable by the invitees?", "Field is required": "Field is required", diff --git a/assets/locales/bo_CN.json b/assets/locales/bo_CN.json index 31f3756a609..7b2f9b317eb 100644 --- a/assets/locales/bo_CN.json +++ b/assets/locales/bo_CN.json @@ -1,7 +1,6 @@ { "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", "Attach": "Attach", "Is it editable by the invitees?": "Is it editable by the invitees?", "Field is required": "Field is required", diff --git a/assets/locales/bs_BA.json b/assets/locales/bs_BA.json index 31f3756a609..7b2f9b317eb 100644 --- a/assets/locales/bs_BA.json +++ b/assets/locales/bs_BA.json @@ -1,7 +1,6 @@ { "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", "Attach": "Attach", "Is it editable by the invitees?": "Is it editable by the invitees?", "Field is required": "Field is required", diff --git a/assets/locales/ca_ES.json b/assets/locales/ca_ES.json index 1a4c98a432a..bcdad8bb3be 100644 --- a/assets/locales/ca_ES.json +++ b/assets/locales/ca_ES.json @@ -18,7 +18,7 @@ "Surveys": "Enquestes", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", + "Course settings": "Configuraci\u00f3 del curs", "Sign in": "Accedeix", "My courses": "La meva llista de cursos", "Attach": "Attach", diff --git a/assets/locales/cs_CZ.json b/assets/locales/cs_CZ.json index 4d53051d1a7..d535a1ee217 100644 --- a/assets/locales/cs_CZ.json +++ b/assets/locales/cs_CZ.json @@ -6,7 +6,6 @@ "Member": "\u010clen", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", "My courses": "Moje kurzy", "Attach": "Attach", "Home": "Dom\u016f", diff --git a/assets/locales/da.json b/assets/locales/da.json index 9b871000f5e..adfa946de31 100644 --- a/assets/locales/da.json +++ b/assets/locales/da.json @@ -12,7 +12,6 @@ "Survey": "Evaluering", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", "My courses": "Min kursusoversigt", "Attach": "Attach", "Home": "Hjem", diff --git a/assets/locales/de.json b/assets/locales/de.json index 8fa59d559b3..bb313100b9d 100644 --- a/assets/locales/de.json +++ b/assets/locales/de.json @@ -18,7 +18,7 @@ "Surveys": "Umfragen", "Tracking": "Tracking", "Course maintenance": "Kurspflege", - "Course setting": "Course setting", + "Course settings": "Kurs-Einstellungen", "Sign in": "Eintragen", "My courses": "Meine Kurse", "Start recording": "Aufnahme starten", diff --git a/assets/locales/el.json b/assets/locales/el.json index 562c06d398d..dd2534fc8a7 100644 --- a/assets/locales/el.json +++ b/assets/locales/el.json @@ -18,7 +18,7 @@ "Surveys": "\u0391\u03c0\u03bf\u03b3\u03c1\u03b1\u03c6\u03ad\u03c2", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", + "Course settings": "\u03a1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03b9\u03c2 \u03bc\u03b1\u03b8\u03ae\u03bc\u03b1\u03c4\u03bf\u03c2", "Sign in": "\u03a3\u03c5\u03bd\u03b4\u03b5\u03b8\u03b5\u03af\u03c4\u03b5", "My courses": "\u03a4\u03b1 \u03bc\u03b1\u03b8\u03ae\u03bc\u03b1\u03c4\u03ac \u03bc\u03bf\u03c5", "Attach": "Attach", diff --git a/assets/locales/en.json b/assets/locales/en.json index 78055a9e8a8..09bfc6a6966 100644 --- a/assets/locales/en.json +++ b/assets/locales/en.json @@ -578,4 +578,4 @@ "Select the destination folder": "Select the destination folder", "Select a folder": "Select a folder", "Move document": "Move document" -} +} \ No newline at end of file diff --git a/assets/locales/eo.json b/assets/locales/eo.json index 46f1303e819..d5556367488 100644 --- a/assets/locales/eo.json +++ b/assets/locales/eo.json @@ -11,7 +11,6 @@ "Member": "Uzanto", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", "My courses": "miajn kursojn", "Attach": "Attach", "Home": "Komenco", diff --git a/assets/locales/es.json b/assets/locales/es.json index bbdd191fd6c..05fe61ad318 100644 --- a/assets/locales/es.json +++ b/assets/locales/es.json @@ -18,7 +18,7 @@ "Surveys": "Encuestas", "Tracking": "Tracking", "Course maintenance": "Mantenimiento del curso", - "Course setting": "Course setting", + "Course settings": "Configuraci\u00f3n del curso", "Sign in": "Ingresar", "My courses": "Mis cursos", "Start recording": "Lanzar la grabaci\u00f3n", @@ -447,7 +447,7 @@ "Cancel": "Cancelar", "Event": "Event", "Are you sure you want to delete this event?": "Are you sure you want to delete this event?", - "Edit introduction": "Edit introduction", + "Edit introduction": "Editar la introduccion", "Show all": "Mostrar todos", "Hide all": "Ocultar todo", "Sort": "Ordenar", diff --git a/assets/locales/eu_ES.json b/assets/locales/eu_ES.json index 0c29132f128..7e1f5503dc5 100644 --- a/assets/locales/eu_ES.json +++ b/assets/locales/eu_ES.json @@ -18,7 +18,7 @@ "Surveys": "Inkestak", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", + "Course settings": "Ikastaroaren ezarpenak", "My courses": "Nire ikastaroak", "Attach": "Attach", "Home": "Hasierakoa", diff --git a/assets/locales/fa_AF.json b/assets/locales/fa_AF.json index 31f3756a609..7b2f9b317eb 100644 --- a/assets/locales/fa_AF.json +++ b/assets/locales/fa_AF.json @@ -1,7 +1,6 @@ { "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", "Attach": "Attach", "Is it editable by the invitees?": "Is it editable by the invitees?", "Field is required": "Field is required", diff --git a/assets/locales/fa_IR.json b/assets/locales/fa_IR.json index 0aa72424ccd..9514cf24cda 100644 --- a/assets/locales/fa_IR.json +++ b/assets/locales/fa_IR.json @@ -18,7 +18,7 @@ "Surveys": "\u0628\u0631\u0631\u0633\u06cc", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", + "Course settings": "\u062a\u0646\u0638\u06cc\u0645\u0627\u062a \u062f\u0631\u0633", "My courses": "\u0641\u0647\u0631\u0633\u062a \u062f\u0631\u0648\u0633 \u0645\u0646", "Attach": "Attach", "Home": "\u0635\u0641\u062d\u0647 \u0627\u0635\u0644\u06cc", diff --git a/assets/locales/fi_FI.json b/assets/locales/fi_FI.json index e3f4a6fa7ee..7e9a95e0a49 100644 --- a/assets/locales/fi_FI.json +++ b/assets/locales/fi_FI.json @@ -15,7 +15,7 @@ "Surveys": "Kyselyt", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", + "Course settings": "Kurssin asetukset", "My courses": "Kurssini", "Attach": "Attach", "Home": "Projektin kotisivu", diff --git a/assets/locales/fo_FO.json b/assets/locales/fo_FO.json index 3274a623ca1..6e9839ae61c 100644 --- a/assets/locales/fo_FO.json +++ b/assets/locales/fo_FO.json @@ -3,7 +3,6 @@ "Links": "Leinkjur", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", "My courses": "M\u00edni skei\u00f0", "Attach": "Attach", "Home": "Heim", diff --git a/assets/locales/fr_FR.json b/assets/locales/fr_FR.json index d5b332facfc..749690322a8 100644 --- a/assets/locales/fr_FR.json +++ b/assets/locales/fr_FR.json @@ -18,7 +18,7 @@ "Surveys": "Enqu\u00eates", "Tracking": "Tracking", "Course maintenance": "Maintenance du cours", - "Course setting": "Course setting", + "Course settings": "Param\u00e8tres du cours", "Sign in": "Connectez-vous", "My courses": "Mes cours", "Start recording": "Lancer l'enregistrement", @@ -447,7 +447,7 @@ "Cancel": "Annuler", "Event": "Event", "Are you sure you want to delete this event?": "Are you sure you want to delete this event?", - "Edit introduction": "Edit introduction", + "Edit introduction": "\u00c9diter l'introduction", "Show all": "Afficher tout", "Hide all": "Masquer tout", "Sort": "Trier", diff --git a/assets/locales/fur.json b/assets/locales/fur.json index a45a620fc10..ac56f66c000 100644 --- a/assets/locales/fur.json +++ b/assets/locales/fur.json @@ -18,7 +18,7 @@ "Surveys": "Surveys", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", + "Course settings": "Course settings", "Sign in": "Sign in", "My courses": "My courses", "Start recording": "Start recording", diff --git a/assets/locales/gl.json b/assets/locales/gl.json index a45a620fc10..ac56f66c000 100644 --- a/assets/locales/gl.json +++ b/assets/locales/gl.json @@ -18,7 +18,7 @@ "Surveys": "Surveys", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", + "Course settings": "Course settings", "Sign in": "Sign in", "My courses": "My courses", "Start recording": "Start recording", diff --git a/assets/locales/he_IL.json b/assets/locales/he_IL.json index dd4c1e79b57..4cda5dbe9b8 100644 --- a/assets/locales/he_IL.json +++ b/assets/locales/he_IL.json @@ -18,7 +18,7 @@ "Surveys": "\u05e1\u05e7\u05e8\u05d9\u05dd", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", + "Course settings": "\u05d4\u05d2\u05d3\u05e8\u05d5\u05ea \u05e7\u05d5\u05e8\u05e1", "Sign in": "\u05db\u05e0\u05d9\u05e1\u05d4", "My courses": "\u05d4\u05e7\u05d5\u05e8\u05e1\u05d9\u05dd \u05e9\u05dc\u05d9", "Start recording": "\u05d4\u05ea\u05d7\u05dc \u05dc\u05d4\u05e7\u05dc\u05d9\u05d8", diff --git a/assets/locales/hi.json b/assets/locales/hi.json index 31f3756a609..7b2f9b317eb 100644 --- a/assets/locales/hi.json +++ b/assets/locales/hi.json @@ -1,7 +1,6 @@ { "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", "Attach": "Attach", "Is it editable by the invitees?": "Is it editable by the invitees?", "Field is required": "Field is required", diff --git a/assets/locales/hr_HR.json b/assets/locales/hr_HR.json index d1cbb434aab..bcd040994a1 100644 --- a/assets/locales/hr_HR.json +++ b/assets/locales/hr_HR.json @@ -12,7 +12,6 @@ "Survey": "Anketa", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", "My courses": "Moji predmeti", "Attach": "Attach", "Home": "Po\u010detna stranica", diff --git a/assets/locales/hu_HU.json b/assets/locales/hu_HU.json index 480cf680641..7b422811f27 100644 --- a/assets/locales/hu_HU.json +++ b/assets/locales/hu_HU.json @@ -12,7 +12,6 @@ "Survey": "K\u00e9rd\u0151\u00edv", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", "My courses": "Kurzusaim", "Attach": "Attach", "Home": "Kezd\u0151lap", diff --git a/assets/locales/id_ID.json b/assets/locales/id_ID.json index fbcdadc933c..47f8642b2b4 100644 --- a/assets/locales/id_ID.json +++ b/assets/locales/id_ID.json @@ -11,7 +11,6 @@ "Member": "Anggota", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", "My courses": "Daftar Mata Kuliah Ku", "Attach": "Attach", "Home": "Home", diff --git a/assets/locales/it.json b/assets/locales/it.json index 1ed7421bfaa..c7b460f4f7d 100644 --- a/assets/locales/it.json +++ b/assets/locales/it.json @@ -18,7 +18,7 @@ "Surveys": "Questionari", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", + "Course settings": "Configurazione del modulo", "Sign in": "Accedi", "My courses": "Corsi", "Start recording": "Inizia la registrazione", diff --git a/assets/locales/ja.json b/assets/locales/ja.json index 75d3925e971..f29eaad5d6d 100644 --- a/assets/locales/ja.json +++ b/assets/locales/ja.json @@ -13,7 +13,7 @@ "Surveys": "\u610f\u898b\u8abf\u67fb", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", + "Course settings": "\u30b3\u30fc\u30b9\u306e\u8a2d\u5b9a", "My courses": "\u30b3\u30fc\u30b9", "Attach": "Attach", "Home": "\u30db\u30fc\u30e0", diff --git a/assets/locales/ka_GE.json b/assets/locales/ka_GE.json index 31f3756a609..7b2f9b317eb 100644 --- a/assets/locales/ka_GE.json +++ b/assets/locales/ka_GE.json @@ -1,7 +1,6 @@ { "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", "Attach": "Attach", "Is it editable by the invitees?": "Is it editable by the invitees?", "Field is required": "Field is required", diff --git a/assets/locales/ko_KR.json b/assets/locales/ko_KR.json index 65f9197a447..9419630a564 100644 --- a/assets/locales/ko_KR.json +++ b/assets/locales/ko_KR.json @@ -8,7 +8,6 @@ "Groups": "\uadf8\ub8f9", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", "My courses": "\ub0b4 \uacfc\uc815", "Attach": "Attach", "Home": "\ud648", diff --git a/assets/locales/lt_LT.json b/assets/locales/lt_LT.json index 6ed9d3410ec..a33c6b59c13 100644 --- a/assets/locales/lt_LT.json +++ b/assets/locales/lt_LT.json @@ -12,7 +12,6 @@ "Survey": "Apklausa", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", "My courses": "Mano kursai", "Attach": "Attach", "Home": "Pagrindinis puslapis", diff --git a/assets/locales/lv_LV.json b/assets/locales/lv_LV.json index abbec4c39f6..8b3044d5b56 100644 --- a/assets/locales/lv_LV.json +++ b/assets/locales/lv_LV.json @@ -18,7 +18,7 @@ "Surveys": "Aptaujas", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", + "Course settings": "Kursa iestat\u012bjumi", "My courses": "Mani kursi", "Attach": "Attach", "Home": "Mana profila lapas s\u0101kums", diff --git a/assets/locales/mk_MK.json b/assets/locales/mk_MK.json index 21631d1c45b..87b580b6ff3 100644 --- a/assets/locales/mk_MK.json +++ b/assets/locales/mk_MK.json @@ -12,7 +12,6 @@ "Survey": "\u0410\u043d\u043a\u0435\u0442\u0430", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", "My courses": "\u041c\u043e\u0438 \u043a\u0443\u0440\u0441\u0435\u0432\u0438", "Attach": "Attach", "Home": "\u041f\u043e\u0447\u0435\u0442\u043e\u043a", diff --git a/assets/locales/ms_MY.json b/assets/locales/ms_MY.json index a02a99f3591..e1b1e341d3d 100644 --- a/assets/locales/ms_MY.json +++ b/assets/locales/ms_MY.json @@ -6,7 +6,6 @@ "Groups": "Kumpulan", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", "My courses": "Senarai Subjek", "Attach": "Attach", "Is it editable by the invitees?": "Is it editable by the invitees?", diff --git a/assets/locales/nl.json b/assets/locales/nl.json index 5e853a14e0f..2cc6ab89cf0 100644 --- a/assets/locales/nl.json +++ b/assets/locales/nl.json @@ -18,7 +18,7 @@ "Surveys": "Enquete", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", + "Course settings": "Cursus instellingen", "Sign in": "Meld aan", "My courses": "Mijn cursussen", "Attach": "Attach", diff --git a/assets/locales/nn_NO.json b/assets/locales/nn_NO.json index 5f7be614d10..482b88b5e79 100644 --- a/assets/locales/nn_NO.json +++ b/assets/locales/nn_NO.json @@ -5,7 +5,6 @@ "Link": "Lenke", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", "My courses": "Mine kurs", "Attach": "Attach", "Is it editable by the invitees?": "Is it editable by the invitees?", diff --git a/assets/locales/oc.json b/assets/locales/oc.json index a45a620fc10..ac56f66c000 100644 --- a/assets/locales/oc.json +++ b/assets/locales/oc.json @@ -18,7 +18,7 @@ "Surveys": "Surveys", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", + "Course settings": "Course settings", "Sign in": "Sign in", "My courses": "My courses", "Start recording": "Start recording", diff --git a/assets/locales/pl_PL.json b/assets/locales/pl_PL.json index 12d2e87cee1..7465e0b681a 100644 --- a/assets/locales/pl_PL.json +++ b/assets/locales/pl_PL.json @@ -18,7 +18,7 @@ "Surveys": "Ankiety", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", + "Course settings": "Ustawienia kursu", "My courses": "Lista moich kurs\u00f3w", "Attach": "Attach", "Home": "Strona g\u0142\u00f3wna", diff --git a/assets/locales/ps.json b/assets/locales/ps.json index 31f3756a609..7b2f9b317eb 100644 --- a/assets/locales/ps.json +++ b/assets/locales/ps.json @@ -1,7 +1,6 @@ { "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", "Attach": "Attach", "Is it editable by the invitees?": "Is it editable by the invitees?", "Field is required": "Field is required", diff --git a/assets/locales/pt_PT.json b/assets/locales/pt_PT.json index d73e979d0e6..a55ffa182ab 100644 --- a/assets/locales/pt_PT.json +++ b/assets/locales/pt_PT.json @@ -18,7 +18,7 @@ "Surveys": "Pesquisas", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", + "Course settings": "Configura\u00e7\u00f5es do curso", "Sign in": "Entrar", "My courses": "Cursos", "Attach": "Attach", diff --git a/assets/locales/qu_PE.json b/assets/locales/qu_PE.json index 63f2c12d9bf..2f9b329e97a 100644 --- a/assets/locales/qu_PE.json +++ b/assets/locales/qu_PE.json @@ -13,7 +13,7 @@ "Survey": "Tapukuy", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", + "Course settings": "Configuraci\u00f3n de la lecci\u00f3n", "My courses": "Noqapaq yachachiykuna", "Attach": "Attach", "Home": "Qallariq", diff --git a/assets/locales/ro_RO.json b/assets/locales/ro_RO.json index 1f6a8fa0a55..2a3ab59f772 100644 --- a/assets/locales/ro_RO.json +++ b/assets/locales/ro_RO.json @@ -17,7 +17,7 @@ "Surveys": "Sondaje", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", + "Course settings": "Setarile cursului", "Sign in": "Conectare", "My courses": "Cursurile mele", "Attach": "Attach", diff --git a/assets/locales/ru_RU.json b/assets/locales/ru_RU.json index 0c0184ab1a5..2c85be730f3 100644 --- a/assets/locales/ru_RU.json +++ b/assets/locales/ru_RU.json @@ -14,7 +14,6 @@ "Surveys": "\u041e\u043f\u0440\u043e\u0441\u043d\u0438\u043a\u0438", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", "My courses": "\u041c\u043e\u0438 \u043a\u0443\u0440\u0441\u044b", "Attach": "Attach", "Home": "\u041d\u0430\u0447\u0430\u043b\u043e (\u0434\u043e\u043c\u043e\u0439)", diff --git a/assets/locales/sk_SK.json b/assets/locales/sk_SK.json index bff3466c1c3..43dabac65b6 100644 --- a/assets/locales/sk_SK.json +++ b/assets/locales/sk_SK.json @@ -18,7 +18,7 @@ "Surveys": "Prieskum", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", + "Course settings": "Nastavenie kurzu", "Sign in": "Prihl\u00e1si\u0165 sa", "My courses": "Moje kurzy", "Attach": "Attach", diff --git a/assets/locales/sl_SI.json b/assets/locales/sl_SI.json index 65012f7819f..caa698a4147 100644 --- a/assets/locales/sl_SI.json +++ b/assets/locales/sl_SI.json @@ -18,7 +18,7 @@ "Surveys": "Evalvacijski vpra\u0161alniki", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", + "Course settings": "Nastavitve te\u010daja", "Sign in": "Vpi\u0161i", "My courses": "Moji te\u010daji", "Start recording": "Pri\u010dni snemanje", diff --git a/assets/locales/so_SO.json b/assets/locales/so_SO.json index 31f3756a609..7b2f9b317eb 100644 --- a/assets/locales/so_SO.json +++ b/assets/locales/so_SO.json @@ -1,7 +1,6 @@ { "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", "Attach": "Attach", "Is it editable by the invitees?": "Is it editable by the invitees?", "Field is required": "Field is required", diff --git a/assets/locales/sr_RS.json b/assets/locales/sr_RS.json index abff76ac73d..437df1ce68c 100644 --- a/assets/locales/sr_RS.json +++ b/assets/locales/sr_RS.json @@ -9,7 +9,7 @@ "Groups": "Grupe", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", + "Course settings": "Pode\u0161avanje kursa", "My courses": "Moja lista kurseva", "Attach": "Attach", "Home": "Po\u010detak", diff --git a/assets/locales/sv_SE.json b/assets/locales/sv_SE.json index e85777726f0..57167b27c5e 100644 --- a/assets/locales/sv_SE.json +++ b/assets/locales/sv_SE.json @@ -14,7 +14,7 @@ "Surveys": "Unders\u00f6kning", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", + "Course settings": "Inst\u00e4llningar", "My courses": "Mina kurser", "Attach": "Attach", "Home": "Hem", diff --git a/assets/locales/sw_KE.json b/assets/locales/sw_KE.json index 31f3756a609..7b2f9b317eb 100644 --- a/assets/locales/sw_KE.json +++ b/assets/locales/sw_KE.json @@ -1,7 +1,6 @@ { "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", "Attach": "Attach", "Is it editable by the invitees?": "Is it editable by the invitees?", "Field is required": "Field is required", diff --git a/assets/locales/th.json b/assets/locales/th.json index a0d2519d9c2..c0ff7538801 100644 --- a/assets/locales/th.json +++ b/assets/locales/th.json @@ -10,7 +10,6 @@ "Groups": "\u0e01\u0e25\u0e38\u0e48\u0e21", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", "My courses": "\u0e23\u0e32\u0e22\u0e27\u0e34\u0e0a\u0e32\u0e02\u0e2d\u0e07\u0e40\u0e23\u0e32", "Attach": "Attach", "Home": "\u0e2b\u0e19\u0e49\u0e32\u0e41\u0e23\u0e01", diff --git a/assets/locales/tl_PH.json b/assets/locales/tl_PH.json index 34b95cd4c1c..14e628f6e5d 100644 --- a/assets/locales/tl_PH.json +++ b/assets/locales/tl_PH.json @@ -13,7 +13,7 @@ "Surveys": "Mga Sarbey", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", + "Course settings": "Mga setting ng kurso", "My courses": "Aking Mga Kurso", "Attach": "Attach", "Home": "Home", diff --git a/assets/locales/tr.json b/assets/locales/tr.json index 73a5a25d1f2..d406487b35f 100644 --- a/assets/locales/tr.json +++ b/assets/locales/tr.json @@ -12,7 +12,6 @@ "Member": "\u00dcye", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", "My courses": "Derslerim", "Attach": "Attach", "Home": "Ana sayfa", diff --git a/assets/locales/uk_UA.json b/assets/locales/uk_UA.json index 22eac68f180..6291a809161 100644 --- a/assets/locales/uk_UA.json +++ b/assets/locales/uk_UA.json @@ -11,7 +11,6 @@ "Survey": "\u041e\u043f\u0438\u0442\u0443\u0432\u0430\u043d\u043d\u044f", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", "My courses": "\u041c\u043e\u0457 \u043a\u0443\u0440\u0441\u0438", "Attach": "Attach", "Home": "\u0414\u043e\u0434\u043e\u043c\u0443", diff --git a/assets/locales/vi_VN.json b/assets/locales/vi_VN.json index e149e3ddba9..72fa9a27ffa 100644 --- a/assets/locales/vi_VN.json +++ b/assets/locales/vi_VN.json @@ -6,7 +6,6 @@ "Groups": "S\u1eeda Nh\u00f3m", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", "My courses": "Danh m\u1ee5c kho\u00e1 h\u1ecdc", "Attach": "Attach", "Is it editable by the invitees?": "Is it editable by the invitees?", diff --git a/assets/locales/xh_ZA.json b/assets/locales/xh_ZA.json index 31f3756a609..7b2f9b317eb 100644 --- a/assets/locales/xh_ZA.json +++ b/assets/locales/xh_ZA.json @@ -1,7 +1,6 @@ { "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", "Attach": "Attach", "Is it editable by the invitees?": "Is it editable by the invitees?", "Field is required": "Field is required", diff --git a/assets/locales/yo_NG.json b/assets/locales/yo_NG.json index 31f3756a609..7b2f9b317eb 100644 --- a/assets/locales/yo_NG.json +++ b/assets/locales/yo_NG.json @@ -1,7 +1,6 @@ { "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", "Attach": "Attach", "Is it editable by the invitees?": "Is it editable by the invitees?", "Field is required": "Field is required", diff --git a/assets/locales/zh_CN.json b/assets/locales/zh_CN.json index 29c823b0f0c..2b1daaa0725 100644 --- a/assets/locales/zh_CN.json +++ b/assets/locales/zh_CN.json @@ -17,7 +17,7 @@ "Surveys": "\u8c03\u67e5", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", + "Course settings": "\u8bfe\u7a0b\u8bbe\u7f6e", "My courses": "\u6211\u7684\u8bfe\u7a0b", "Attach": "Attach", "Home": "\u4e3b\u9875", diff --git a/assets/locales/zh_TW.json b/assets/locales/zh_TW.json index b3e2ae9bb3c..54eca795259 100644 --- a/assets/locales/zh_TW.json +++ b/assets/locales/zh_TW.json @@ -12,7 +12,6 @@ "Survey": "\u7d71\u8a08\u8abf\u67e5", "Tracking": "Tracking", "Course maintenance": "Course maintenance", - "Course setting": "Course setting", "My courses": "\u6211\u7684\u8ab2\u7a0b", "Attach": "Attach", "Home": "\u9996\u9801", From 248d6fd0a6ffcdc22386dc7bcf1d3dbfa0b65eb8 Mon Sep 17 00:00:00 2001 From: Christian Beeznest Date: Tue, 3 Dec 2024 18:20:24 -0500 Subject: [PATCH 10/11] Internal: Fix score calculation across reports and interfaces - refs BT#22096 --- public/main/inc/lib/TrackingCourseLog.php | 6 +----- public/main/inc/lib/exercise.lib.php | 4 +++- public/main/inc/lib/tracking.lib.php | 9 +++------ src/CourseBundle/Repository/CQuizRepository.php | 4 +++- 4 files changed, 10 insertions(+), 13 deletions(-) diff --git a/public/main/inc/lib/TrackingCourseLog.php b/public/main/inc/lib/TrackingCourseLog.php index 636d6f9a196..e458ae55b61 100644 --- a/public/main/inc/lib/TrackingCourseLog.php +++ b/public/main/inc/lib/TrackingCourseLog.php @@ -664,11 +664,7 @@ public static function getUserData( $totalSurveys = 0; $totalExercises = ExerciseLib::get_all_exercises( $courseInfo, - $sessionId, - false, - null, - false, - 3 + $sessionId ); if (empty($sessionId)) { diff --git a/public/main/inc/lib/exercise.lib.php b/public/main/inc/lib/exercise.lib.php index f7a339b77bb..c363f6cef2d 100644 --- a/public/main/inc/lib/exercise.lib.php +++ b/public/main/inc/lib/exercise.lib.php @@ -3000,7 +3000,9 @@ public static function get_all_exercises( $repo = Container::getQuizRepository(); - return $repo->findAllByCourse($course, $session, (string) $search, $active); + return $repo->findAllByCourse($course, $session, (string) $search, $active) + ->getQuery() + ->getResult(); } /** diff --git a/public/main/inc/lib/tracking.lib.php b/public/main/inc/lib/tracking.lib.php index 763af042d25..8373faa6874 100644 --- a/public/main/inc/lib/tracking.lib.php +++ b/public/main/inc/lib/tracking.lib.php @@ -2640,11 +2640,6 @@ public static function get_exercise_student_average_best_attempt( $sessionId ) { $result = 0; - - if ($exercise_list instanceof \Doctrine\ORM\QueryBuilder) { - $exercise_list = $exercise_list->getQuery()->getResult(); - } - if (!empty($exercise_list) && (is_array($exercise_list) || $exercise_list instanceof \Countable)) { foreach ($exercise_list as $exercise_data) { $exercise_id = $exercise_data->getIid(); @@ -2652,7 +2647,8 @@ public static function get_exercise_student_average_best_attempt( $user_id, $exercise_id, $courseId, - $sessionId + $sessionId, + false ); if (!empty($best_attempt) && !empty($best_attempt['max_score'])) { @@ -2940,6 +2936,7 @@ public static function get_avg_student_score( WHERE lp_id IN (".implode(',', $lp_list).") $condition_user1 + $sessionCondition GROUP BY lp_id, user_id"; //AND session_id = $sessionId diff --git a/src/CourseBundle/Repository/CQuizRepository.php b/src/CourseBundle/Repository/CQuizRepository.php index cf95c8a6899..08f20b322db 100644 --- a/src/CourseBundle/Repository/CQuizRepository.php +++ b/src/CourseBundle/Repository/CQuizRepository.php @@ -40,7 +40,9 @@ public function findAllByCourse( $this->addCategoryQueryBuilder($categoryId, $qb); $this->addActiveQueryBuilder($active, $qb); $this->addNotDeletedQueryBuilder($qb); - $this->addTitleQueryBuilder($title, $qb); + if (!empty($title)) { + $this->addTitleQueryBuilder($title, $qb); + } return $qb; } From caa10feedbdfd6892c41c5ceca47c643a2cfe70a Mon Sep 17 00:00:00 2001 From: Angel Fernando Quiroz Campos <1697880+AngelFQC@users.noreply.github.com> Date: Wed, 4 Dec 2024 12:46:52 -0500 Subject: [PATCH 11/11] Fix request to get course before enter the home page - refs BT#22265 --- assets/vue/router/index.js | 2 +- assets/vue/services/courseService.js | 43 +++++++++++----------------- 2 files changed, 18 insertions(+), 27 deletions(-) diff --git a/assets/vue/router/index.js b/assets/vue/router/index.js index aa70794570a..edc7527b349 100644 --- a/assets/vue/router/index.js +++ b/assets/vue/router/index.js @@ -116,7 +116,7 @@ const router = createRouter({ return false } - const course = await courseService.getCourseDetails(courseId) + const course = await courseService.findById(courseId, { sid: sessionId }) if (!course) { return false } diff --git a/assets/vue/services/courseService.js b/assets/vue/services/courseService.js index 60e65ec3ca8..1f6e10737bc 100644 --- a/assets/vue/services/courseService.js +++ b/assets/vue/services/courseService.js @@ -4,6 +4,13 @@ import baseService from "./baseService" export default { find: baseService.get, + /** + * @param {number} cid + * @param {object} params + * @returns {Promise} + */ + findById: async (cid, params) => baseService.get(`/api/courses/${cid}`, params), + /** * @param {number} courseId * @param {number=} sessionId @@ -107,22 +114,6 @@ export default { })) }, - /** - * Fetches course details by course ID. - * - * @param {number} courseId - The ID of the course. - * @returns {Promise} - The course details or null if an error occurs. - */ - getCourseDetails: async (courseId) => { - try { - const response = await api.get(`/api/courses/${courseId}`) - return response.data - } catch (error) { - console.error("Error fetching course details:", error) - return null - } - }, - /** * Retrieves the ID of the auto-launchable exercise in a course, if configured. * @@ -136,16 +127,16 @@ export default { params: { sid: sessionId, }, - }); + }) if (data && data.exerciseId) { - return data.exerciseId; + return data.exerciseId } - return null; + return null } catch (error) { - console.error("Error fetching auto-launch exercise ID:", error); - return null; + console.error("Error fetching auto-launch exercise ID:", error) + return null } }, /** @@ -161,16 +152,16 @@ export default { params: { sid: sessionId, }, - }); + }) if (data && data.lpId) { - return data.lpId; + return data.lpId } - return null; + return null } catch (error) { - console.error("Error fetching auto-launch LP ID:", error); - return null; + console.error("Error fetching auto-launch LP ID:", error) + return null } }, }