diff --git a/main/auth/external_login/facebook.inc.php b/main/auth/external_login/facebook.inc.php index 1b7132743a..c0b0cc2c47 100755 --- a/main/auth/external_login/facebook.inc.php +++ b/main/auth/external_login/facebook.inc.php @@ -171,7 +171,6 @@ function facebookConnect() /** * Get facebook login url for the platform. * - * @return string * @throws FacebookSDKException */ function facebookGetLoginUrl(): string diff --git a/main/auth/external_login/functions.inc.php b/main/auth/external_login/functions.inc.php index 3eeb2ee064..30347dbe0a 100755 --- a/main/auth/external_login/functions.inc.php +++ b/main/auth/external_login/functions.inc.php @@ -166,25 +166,25 @@ function external_add_user($u) * new_user array. * * @param array $new_user associative array with the value to upgrade - * WARNING user_id key is MANDATORY - * Possible keys are : - * - firstname - * - lastname - * - username - * - auth_source - * - email - * - status - * - official_code - * - phone - * - picture_uri - * - expiration_date - * - active - * - creator_id - * - hr_dept_id - * - extra : array of custom fields - * - language - * - courses : string of all courses code separated by '|' - * - admin : boolean + * WARNING user_id key is MANDATORY + * Possible keys are : + * - firstname + * - lastname + * - username + * - auth_source + * - email + * - status + * - official_code + * - phone + * - picture_uri + * - expiration_date + * - active + * - creator_id + * - hr_dept_id + * - extra : array of custom fields + * - language + * - courses : string of all courses code separated by '|' + * - admin : boolean * * @author ndiechburg * */ diff --git a/main/exercise/export/aiken/aiken_import.inc.php b/main/exercise/export/aiken/aiken_import.inc.php index 053fce3c5c..bea43d0940 100755 --- a/main/exercise/export/aiken/aiken_import.inc.php +++ b/main/exercise/export/aiken/aiken_import.inc.php @@ -70,7 +70,7 @@ function generateAikenForm() if ($hasSingleApi) { $apiName = $availableApis[$configuredApi] ?? $configuredApi; $form->addHtml('
' - . sprintf(get_lang('UsingAIProviderX'), ''.htmlspecialchars($apiName).'').'
'); + .sprintf(get_lang('UsingAIProviderX'), ''.htmlspecialchars($apiName).'').''); } $form->addElement('text', 'quiz_name', get_lang('QuestionsTopic')); @@ -105,7 +105,7 @@ function generateAikenForm() var quizName = $("[name=\'quiz_name\']").val(); var nroQ = parseInt($("[name=\'nro_questions\']").val()); var qType = $("[name=\'question_type\']").val();' - . (!$hasSingleApi ? 'var provider = $("[name=\'ai_provider\']").val();' : 'var provider = "' . $configuredApi . '";') . + .(!$hasSingleApi ? 'var provider = $("[name=\'ai_provider\']").val();' : 'var provider = "'.$configuredApi.'";'). 'var valid = (quizName != \'\' && nroQ > 0); if (valid) { btnGenerate.attr("disabled", true); diff --git a/main/inc/lib/Compilatio.php b/main/inc/lib/Compilatio.php index 0fea4e5ef7..6412275ce0 100644 --- a/main/inc/lib/Compilatio.php +++ b/main/inc/lib/Compilatio.php @@ -208,7 +208,7 @@ class Compilatio if (isset($dataDocument['analyses'][$anasim]['state'])) { $documentInfo['analysis_status'] = $dataDocument['analyses'][$anasim]['state']; } - + if (isset($dataDocument['light_reports'][$anasim]['scores']['global_score_percent'])) { $documentInfo['report_percent'] = $dataDocument['light_reports'][$anasim]['scores']['global_score_percent']; } diff --git a/main/inc/lib/api.lib.php b/main/inc/lib/api.lib.php index a3fafffe4c..a2b4ee6c28 100755 --- a/main/inc/lib/api.lib.php +++ b/main/inc/lib/api.lib.php @@ -4033,7 +4033,7 @@ function api_not_allowed( // Check if a custom file (login.tpl) exists for custompages included overrides if ((!isset($user_id) || api_is_anonymous()) && CustomPages::enabled()) { $customLoginTemplate = Template::findTemplateFilePath('custompage/login.tpl'); - if (file_exists(api_get_path(SYS_TEMPLATE_PATH) . $customLoginTemplate)) { + if (file_exists(api_get_path(SYS_TEMPLATE_PATH).$customLoginTemplate)) { if (empty($_SESSION['request_uri'])) { $_SESSION['request_uri'] = $_SERVER['REQUEST_URI']; } diff --git a/main/inc/lib/formvalidator/Element/HtmlEditor.php b/main/inc/lib/formvalidator/Element/HtmlEditor.php index ef96e51c0f..870176f5ee 100644 --- a/main/inc/lib/formvalidator/Element/HtmlEditor.php +++ b/main/inc/lib/formvalidator/Element/HtmlEditor.php @@ -112,9 +112,6 @@ class HtmlEditor extends HTML_QuickForm_textarea return $result; } - /** - * @return string|null - */ public function getValue(): ?string { return RemoveOnAttributes::filter($this->_value); diff --git a/main/inc/lib/glossary.lib.php b/main/inc/lib/glossary.lib.php index 78daf5cc18..4127748f5f 100755 --- a/main/inc/lib/glossary.lib.php +++ b/main/inc/lib/glossary.lib.php @@ -132,12 +132,11 @@ class GlossaryManager * This functions stores the glossary in the database. * * @param array $values Array of title + description (name => $title, description => $comment) - * @param bool $showMessage - * - * @return bool|int Term id on success, false on failure * * @throws \Doctrine\ORM\ORMException * @throws \Doctrine\ORM\OptimisticLockException + * + * @return bool|int Term id on success, false on failure */ public static function save_glossary(array $values, bool $showMessage = true) { @@ -170,7 +169,7 @@ class GlossaryManager ->setDescription($values['description'] ?? "") ->setDisplayOrder($max_glossary_item + 1) ->setSessionId($session_id); - ; + Database::getManager()->persist($glossary); Database::getManager()->flush(); diff --git a/main/lp/LpAiHelper.php b/main/lp/LpAiHelper.php index 9d56ea510e..1893781745 100644 --- a/main/lp/LpAiHelper.php +++ b/main/lp/LpAiHelper.php @@ -74,7 +74,7 @@ class LpAiHelper var wordsCount = parseInt($("[name=\'words_count\']").val()); var addTests = $("#add-lp-quiz").is(":checked"); var nroQuestions = parseInt($("[name=\'nro_questions\']").val()); - var provider = "' . $configuredApi . '"; + var provider = "'.$configuredApi.'"; if (lpName && nroItems > 0 && wordsCount > 0) { if (addTests && (nroQuestions <= 0 || nroQuestions > 5)) { diff --git a/main/lp/learnpath.class.php b/main/lp/learnpath.class.php index dc02c799ce..3a97e98c95 100755 --- a/main/lp/learnpath.class.php +++ b/main/lp/learnpath.class.php @@ -4860,12 +4860,8 @@ class learnpath /** * Check if the learnpath category is visible for a user. * - * @param CLpCategory|null $category - * @param User $user * @param int $courseId * @param int $sessionId - * - * @return bool */ public static function categoryIsVisibleForStudent( ?CLpCategory $category, diff --git a/main/session/resume_session.php b/main/session/resume_session.php index 540234861c..745b822458 100644 --- a/main/session/resume_session.php +++ b/main/session/resume_session.php @@ -370,6 +370,7 @@ if (!empty($userList)) { $valueA = strtotime($a['registered_at']); $valueB = strtotime($b['registered_at']); } + return $sortOrder === SORT_ASC ? $valueA <=> $valueB : $valueB <=> $valueA; }); diff --git a/plugin/ai_helper/AiHelperPlugin.php b/plugin/ai_helper/AiHelperPlugin.php index 2b50655d4a..868d562690 100644 --- a/plugin/ai_helper/AiHelperPlugin.php +++ b/plugin/ai_helper/AiHelperPlugin.php @@ -3,7 +3,8 @@ use Chamilo\PluginBundle\Entity\AiHelper\Requests; use Doctrine\ORM\Tools\SchemaTool; -require_once __DIR__ . '/src/deepseek/DeepSeek.php'; + +require_once __DIR__.'/src/deepseek/DeepSeek.php'; /** * Description of AiHelperPlugin. * @@ -112,6 +113,7 @@ class AiHelperPlugin extends Plugin if (isset($result['error'])) { $errorMessage = $result['error']['message'] ?? 'Unknown error'; error_log("OpenAI Error: $errorMessage"); + return [ 'error' => true, 'message' => $errorMessage, @@ -132,11 +134,10 @@ class AiHelperPlugin extends Plugin } return $resultText ?: 'No response generated.'; - } catch (Exception $e) { return [ 'error' => true, - 'message' => 'An error occurred while connecting to OpenAI: ' . $e->getMessage(), + 'message' => 'An error occurred while connecting to OpenAI: '.$e->getMessage(), ]; } } @@ -179,8 +180,9 @@ class AiHelperPlugin extends Plugin $response = curl_exec($ch); if ($response === false) { - error_log('cURL error: ' . curl_error($ch)); + error_log('cURL error: '.curl_error($ch)); curl_close($ch); + return ['error' => true, 'message' => 'Request to AI provider failed.']; } @@ -211,12 +213,14 @@ class AiHelperPlugin extends Plugin /** * Generate questions based on the selected AI provider. * - * @param int $nQ Number of questions - * @param string $lang Language for the questions - * @param string $topic Topic of the questions + * @param int $nQ Number of questions + * @param string $lang Language for the questions + * @param string $topic Topic of the questions * @param string $questionType Type of questions (e.g., 'multiple_choice') - * @return string Questions generated in Aiken format + * * @throws Exception If an error occurs + * + * @return string Questions generated in Aiken format */ public function generateQuestions(int $nQ, string $lang, string $topic, string $questionType = 'multiple_choice'): string { @@ -232,61 +236,6 @@ class AiHelperPlugin extends Plugin } } - /** - * Generate questions using OpenAI. - */ - private function generateOpenAiQuestions(int $nQ, string $lang, string $topic, string $questionType): string - { - $prompt = sprintf( - 'Generate %d "%s" questions in Aiken format in the %s language about "%s", making sure there is a \'ANSWER\' line for each question. \'ANSWER\' lines must only mention the letter of the correct answer, not the full answer text and not a parenthesis. The line starting with \'ANSWER\' must not be separated from the last possible answer by a blank line. Each answer starts with an uppercase letter, a dot, one space and the answer text without quotes. Include an \'ANSWER_EXPLANATION\' line after the \'ANSWER\' line for each question. The terms between single quotes above must not be translated. There must be a blank line between each question.', - $nQ, - $questionType, - $lang, - $topic - ); - - $result = $this->openAiGetCompletionText($prompt, 'quiz'); - if (isset($result['error']) && true === $result['error']) { - throw new Exception($result['message']); - } - - return $result; - } - - /** - * Generate questions using DeepSeek. - */ - private function generateDeepSeekQuestions(int $nQ, string $lang, string $topic, string $questionType): string - { - $apiKey = $this->get('api_key'); - $prompt = sprintf( - 'Generate %d "%s" questions in Aiken format in the %s language about "%s", making sure there is a \'ANSWER\' line for each question. \'ANSWER\' lines must only mention the letter of the correct answer, not the full answer text and not a parenthesis. The line starting with \'ANSWER\' must not be separated from the last possible answer by a blank line. Each answer starts with an uppercase letter, a dot, one space and the answer text without quotes. Include an \'ANSWER_EXPLANATION\' line after the \'ANSWER\' line for each question. The terms between single quotes above must not be translated. There must be a blank line between each question.', - $nQ, - $questionType, - $lang, - $topic - ); - $payload = [ - 'model' => 'deepseek-chat', - 'messages' => [ - [ - 'role' => 'system', - 'content' => 'You are a helpful assistant that generates Aiken format questions.', - ], - [ - 'role' => 'user', - 'content' => $prompt, - ], - ], - 'stream' => false, - ]; - - $deepSeek = new DeepSeek($apiKey); - $response = $deepSeek->generateQuestions($payload); - - return $response; - } - /** * Validates tokens limit of a user per current month. */ @@ -414,4 +363,59 @@ class AiHelperPlugin extends Plugin ] ); } + + /** + * Generate questions using OpenAI. + */ + private function generateOpenAiQuestions(int $nQ, string $lang, string $topic, string $questionType): string + { + $prompt = sprintf( + 'Generate %d "%s" questions in Aiken format in the %s language about "%s", making sure there is a \'ANSWER\' line for each question. \'ANSWER\' lines must only mention the letter of the correct answer, not the full answer text and not a parenthesis. The line starting with \'ANSWER\' must not be separated from the last possible answer by a blank line. Each answer starts with an uppercase letter, a dot, one space and the answer text without quotes. Include an \'ANSWER_EXPLANATION\' line after the \'ANSWER\' line for each question. The terms between single quotes above must not be translated. There must be a blank line between each question.', + $nQ, + $questionType, + $lang, + $topic + ); + + $result = $this->openAiGetCompletionText($prompt, 'quiz'); + if (isset($result['error']) && true === $result['error']) { + throw new Exception($result['message']); + } + + return $result; + } + + /** + * Generate questions using DeepSeek. + */ + private function generateDeepSeekQuestions(int $nQ, string $lang, string $topic, string $questionType): string + { + $apiKey = $this->get('api_key'); + $prompt = sprintf( + 'Generate %d "%s" questions in Aiken format in the %s language about "%s", making sure there is a \'ANSWER\' line for each question. \'ANSWER\' lines must only mention the letter of the correct answer, not the full answer text and not a parenthesis. The line starting with \'ANSWER\' must not be separated from the last possible answer by a blank line. Each answer starts with an uppercase letter, a dot, one space and the answer text without quotes. Include an \'ANSWER_EXPLANATION\' line after the \'ANSWER\' line for each question. The terms between single quotes above must not be translated. There must be a blank line between each question.', + $nQ, + $questionType, + $lang, + $topic + ); + $payload = [ + 'model' => 'deepseek-chat', + 'messages' => [ + [ + 'role' => 'system', + 'content' => 'You are a helpful assistant that generates Aiken format questions.', + ], + [ + 'role' => 'user', + 'content' => $prompt, + ], + ], + 'stream' => false, + ]; + + $deepSeek = new DeepSeek($apiKey); + $response = $deepSeek->generateQuestions($payload); + + return $response; + } } diff --git a/plugin/ai_helper/src/deepseek/DeepSeek.php b/plugin/ai_helper/src/deepseek/DeepSeek.php index 4f80dcbdd2..e50494f48e 100644 --- a/plugin/ai_helper/src/deepseek/DeepSeek.php +++ b/plugin/ai_helper/src/deepseek/DeepSeek.php @@ -21,8 +21,10 @@ class DeepSeek * Generate questions using the DeepSeek API. * * @param array $payload Data to send to the API - * @return string Decoded response from the API + * * @throws Exception If an error occurs during the request + * + * @return string Decoded response from the API */ public function generateQuestions(array $payload): string { @@ -51,11 +53,13 @@ class DeepSeek /** * Send a request to the DeepSeek API. * - * @param string $url Endpoint to send the request to + * @param string $url Endpoint to send the request to * @param string $method HTTP method (e.g., GET, POST) - * @param array $data Data to send as JSON - * @return string Raw response from the API + * @param array $data Data to send as JSON + * * @throws Exception If a cURL error occurs + * + * @return string Raw response from the API */ private function sendRequest(string $url, string $method, array $data = []): string { diff --git a/plugin/ai_helper/src/deepseek/DeepSeekUrl.php b/plugin/ai_helper/src/deepseek/DeepSeekUrl.php index d2df1ff897..5d9b1453d9 100644 --- a/plugin/ai_helper/src/deepseek/DeepSeekUrl.php +++ b/plugin/ai_helper/src/deepseek/DeepSeekUrl.php @@ -12,6 +12,6 @@ class DeepSeekUrl */ public static function completionsUrl(): string { - return self::BASE_URL . '/completions'; + return self::BASE_URL.'/completions'; } } diff --git a/plugin/ai_helper/tool/answers.php b/plugin/ai_helper/tool/answers.php index 97263c355c..c60049a509 100644 --- a/plugin/ai_helper/tool/answers.php +++ b/plugin/ai_helper/tool/answers.php @@ -22,10 +22,9 @@ try { 'text' => trim($resultText), ]); } catch (Exception $e) { - error_log("Error: " . $e->getMessage()); + error_log("Error: ".$e->getMessage()); echo json_encode([ 'success' => false, 'text' => $e->getMessage(), ]); } - diff --git a/plugin/ai_helper/tool/learnpath.php b/plugin/ai_helper/tool/learnpath.php index 7a49e10479..05f20c8dda 100644 --- a/plugin/ai_helper/tool/learnpath.php +++ b/plugin/ai_helper/tool/learnpath.php @@ -22,7 +22,6 @@ if (!in_array($apiName, array_keys($apiList))) { exit; } - $courseLanguage = (string) $_REQUEST['language']; $chaptersCount = (int) $_REQUEST['nro_items']; $topic = (string) $_REQUEST['lp_name']; diff --git a/plugin/azure_active_directory/src/AzureActiveDirectory.php b/plugin/azure_active_directory/src/AzureActiveDirectory.php index b3e4b86723..5bdb466225 100644 --- a/plugin/azure_active_directory/src/AzureActiveDirectory.php +++ b/plugin/azure_active_directory/src/AzureActiveDirectory.php @@ -384,6 +384,29 @@ class AzureActiveDirectory extends Plugin ]; } + public function getSyncState(string $title): ?AzureSyncState + { + $stateRepo = Database::getManager()->getRepository(AzureSyncState::class); + + return $stateRepo->findOneBy(['title' => $title]); + } + + public function saveSyncState(string $title, $value) + { + $state = $this->getSyncState($title); + + if (!$state) { + $state = new AzureSyncState(); + $state->setTitle($title); + + Database::getManager()->persist($state); + } + + $state->setValue($value); + + Database::getManager()->flush(); + } + /** * @throws Exception */ @@ -425,27 +448,4 @@ class AzureActiveDirectory extends Plugin $extra, ]; } - - public function getSyncState(string $title): ?AzureSyncState - { - $stateRepo = Database::getManager()->getRepository(AzureSyncState::class); - - return $stateRepo->findOneBy(['title' => $title]); - } - - public function saveSyncState(string $title, $value) - { - $state = $this->getSyncState($title); - - if (!$state) { - $state = new AzureSyncState(); - $state->setTitle($title); - - Database::getManager()->persist($state); - } - - $state->setValue($value); - - Database::getManager()->flush(); - } } diff --git a/plugin/azure_active_directory/src/Entity/AzureSyncState.php b/plugin/azure_active_directory/src/Entity/AzureSyncState.php index a61016770b..bda48f6fd6 100644 --- a/plugin/azure_active_directory/src/Entity/AzureSyncState.php +++ b/plugin/azure_active_directory/src/Entity/AzureSyncState.php @@ -21,8 +21,6 @@ class AzureSyncState public const USERGROUPS_DATALINK = 'usergroups_datalink'; /** - * @var int - * * @ORM\Column(name="id", type="integer") * @ORM\Id() * @ORM\GeneratedValue() @@ -30,15 +28,11 @@ class AzureSyncState private int $id = 0; /** - * @var string - * * @ORM\Column(name="title", type="string") */ private string $title; /** - * @var string - * * @ORM\Column(name="value", type="text") */ private string $value;