diff --git a/public/main/user/user.php b/public/main/user/user.php index f36cd43b091..2ebc2ce0f76 100644 --- a/public/main/user/user.php +++ b/public/main/user/user.php @@ -481,19 +481,26 @@ } } -// Global hosting limit: precompute a simple snapshot for UI purposes only. -$globalLimitUsersPerCourse = 0; -$currentUsersForGlobalLimit = 0; -$courseIsFullForGlobalLimit = false; +// Users-per-course limit: precompute a simple snapshot for UI purposes only. +$usersPerCourseLimit = 0; +$currentUsersForCourseLimit = 0; +$courseIsFullForUsersPerCourseLimit = false; if ($canEdit && 0 === (int) $sessionId) { - // Read the global limit through CourseManager helper to keep logic centralized. - $globalLimitUsersPerCourse = (int) CourseManager::getGlobalUsersPerCourseLimit(); + if (method_exists('CourseManager', 'getEffectiveUsersPerCourseLimit')) { + $usersPerCourseLimit = (int) CourseManager::getEffectiveUsersPerCourseLimit($courseId); + } else { + $usersPerCourseLimit = (int) CourseManager::getGlobalUsersPerCourseLimit(); + } + + if ($usersPerCourseLimit > 0) { + if (method_exists('CourseManager', 'countStudentsForUsersPerCourseLimit')) { + $currentUsersForCourseLimit = (int) CourseManager::countStudentsForUsersPerCourseLimit($courseId); + } else { + $currentUsersForCourseLimit = (int) CourseManager::countUsersForGlobalLimit($courseId); + } - if ($globalLimitUsersPerCourse > 0) { - // Count current users for the limit, excluding HR relation type. - $currentUsersForGlobalLimit = (int) CourseManager::countUsersForGlobalLimit($courseId); - $courseIsFullForGlobalLimit = $currentUsersForGlobalLimit >= $globalLimitUsersPerCourse; + $courseIsFullForUsersPerCourseLimit = $currentUsersForCourseLimit >= $usersPerCourseLimit; } } @@ -628,10 +635,10 @@ $actionsLeft = ''; if ($canEdit) { - // When the global hosting limit is enabled and already reached (no session), + // When the users-per-course limit is enabled and already reached (no session), // we hide the "Add" icon to avoid actions that would add more users. $canShowAddIcon = true; - if (0 === (int) $sessionId && $globalLimitUsersPerCourse > 0 && $courseIsFullForGlobalLimit) { + if (0 === (int) $sessionId && $usersPerCourseLimit > 0 && $courseIsFullForUsersPerCourseLimit) { $canShowAddIcon = false; } @@ -683,15 +690,20 @@ echo UserManager::getUserSubscriptionTab($selectedTab); echo Display::toolbarAction('toolbar', [$actionsLeft, $actionsRight]); - // When the course is full according to the global limit (no session), + // When the course is full according to the effective limit (global or BuyCourses), // show an explicit warning so teachers understand why the Add icon is hidden. - if ($canEdit && 0 === (int) $sessionId && $globalLimitUsersPerCourse > 0 && $courseIsFullForGlobalLimit) { - $msg = sprintf( - get_lang( - 'This course already reached the global limit of %d users set by the administrators. To add more users, please ask an administrator to raise this limit or use sessions.' - ), - $globalLimitUsersPerCourse - ); + if ($canEdit && 0 === (int) $sessionId && $usersPerCourseLimit > 0 && $courseIsFullForUsersPerCourseLimit) { + if (method_exists('CourseManager', 'getUsersPerCourseLimitCancelMessage')) { + $msg = CourseManager::getUsersPerCourseLimitCancelMessage($courseId); + } else { + $msg = sprintf( + get_lang( + 'This operation would exceed the limit of %d users allowed for this course.' + ), + $usersPerCourseLimit + ); + } + echo Display::return_message($msg, 'warning'); } } diff --git a/public/plugin/BuyCourses/src/buy_course_plugin.class.php b/public/plugin/BuyCourses/src/buy_course_plugin.class.php index 67e90162b57..1798d8f9bf3 100644 --- a/public/plugin/BuyCourses/src/buy_course_plugin.class.php +++ b/public/plugin/BuyCourses/src/buy_course_plugin.class.php @@ -6246,19 +6246,45 @@ private function saveUserExtraFieldTextValue(int $userId, int $extraFieldId, str $checkResult = Database::query($checkSql); if ($checkResult && Database::num_rows($checkResult) > 0) { - Database::query("UPDATE $table - SET $valueColumn = '$escapedValue' - WHERE field_id = $extraFieldId - AND item_id = $userId"); + $updateData = [ + $valueColumn => $value, + ]; + + if (in_array('updated_at', $this->getExtraFieldValueTimestampColumns(), true)) { + $updateData['updated_at'] = api_get_utc_datetime(); + } + + Database::update( + $table, + $updateData, + [ + 'field_id = ? AND item_id = ?' => [$extraFieldId, $userId], + ] + ); return; } - Database::insert($table, [ + $insertData = [ 'field_id' => $extraFieldId, 'item_id' => $userId, $valueColumn => $value, - ]); + ]; + + $timestampColumns = $this->getExtraFieldValueTimestampColumns(); + if (!empty($timestampColumns)) { + $now = api_get_utc_datetime(); + + if (in_array('created_at', $timestampColumns, true)) { + $insertData['created_at'] = $now; + } + + if (in_array('updated_at', $timestampColumns, true)) { + $insertData['updated_at'] = $now; + } + } + + Database::insert($table, $insertData); } /** @@ -9194,6 +9220,35 @@ private function getExtraFieldValueColumn(): string throw new RuntimeException('No supported value column found in extra_field_values.'); } + /** + * Return timestamp columns available on extra_field_values. + * Some Chamilo 2 installations enforce NOT NULL created_at/updated_at. + * + * @return string[] + */ + private function getExtraFieldValueTimestampColumns(): array + { + static $columns = null; + + if (null !== $columns) { + return $columns; + } + + $columns = []; + $table = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES); + $result = Database::query("SHOW COLUMNS FROM $table"); + + while ($row = Database::fetch_array($result, 'ASSOC')) { + $fieldName = (string) ($row['Field'] ?? ''); + + if (in_array($fieldName, ['created_at', 'updated_at'], true)) { + $columns[] = $fieldName; + } + } + + return $columns; + } + /** * Return all benefit relations configured for a service. */ @@ -9237,9 +9292,7 @@ public function saveUserExtraFieldJsonValue(int $userId, int $extraFieldId, arra $table = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES); $valueColumn = $this->getExtraFieldValueColumn(); - $jsonValue = Database::escape_string( - json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) - ); + $jsonValue = json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); $checkSql = "SELECT id FROM $table @@ -9249,11 +9302,21 @@ public function saveUserExtraFieldJsonValue(int $userId, int $extraFieldId, arra $checkResult = Database::query($checkSql); if ($checkResult && Database::num_rows($checkResult) > 0) { - $sql = "UPDATE $table - SET $valueColumn = '$jsonValue' - WHERE field_id = $extraFieldId - AND item_id = $userId"; - Database::query($sql); + $updateData = [ + $valueColumn => $jsonValue, + ]; + + if (in_array('updated_at', $this->getExtraFieldValueTimestampColumns(), true)) { + $updateData['updated_at'] = api_get_utc_datetime(); + } + + Database::update( + $table, + $updateData, + [ + 'field_id = ? AND item_id = ?' => [$extraFieldId, $userId], + ] + ); return; } @@ -9264,6 +9327,19 @@ public function saveUserExtraFieldJsonValue(int $userId, int $extraFieldId, arra $valueColumn => $jsonValue, ]; + $timestampColumns = $this->getExtraFieldValueTimestampColumns(); + if (!empty($timestampColumns)) { + $now = api_get_utc_datetime(); + + if (in_array('created_at', $timestampColumns, true)) { + $insertData['created_at'] = $now; + } + + if (in_array('updated_at', $timestampColumns, true)) { + $insertData['updated_at'] = $now; + } + } + Database::insert($table, $insertData); } diff --git a/public/plugin/BuyCourses/view/catalog.tpl b/public/plugin/BuyCourses/view/catalog.tpl index a1c670a3701..b42e73c6651 100644 --- a/public/plugin/BuyCourses/view/catalog.tpl +++ b/public/plugin/BuyCourses/view/catalog.tpl @@ -498,7 +498,7 @@ {% if service.has_blocking_sale|default(false) %} {{ 'Already purchased'|get_lang }} diff --git a/public/plugin/BuyCourses/view/index_products.tpl b/public/plugin/BuyCourses/view/index_products.tpl index b5a9dbf69a4..7fbec5c5b47 100644 --- a/public/plugin/BuyCourses/view/index_products.tpl +++ b/public/plugin/BuyCourses/view/index_products.tpl @@ -237,7 +237,7 @@ {% if service.has_blocking_sale|default(false) %} - + {{ 'Already purchased'|get_lang }}