diff --git a/public/main/auth/registration.php b/public/main/auth/registration.php index fbfebcb6f0b..0910115cb44 100644 --- a/public/main/auth/registration.php +++ b/public/main/auth/registration.php @@ -5,6 +5,7 @@ use Chamilo\CoreBundle\Entity\PageCategory; use Chamilo\CoreBundle\Entity\User; use Chamilo\CoreBundle\Entity\UserAuthSource; +use Chamilo\CoreBundle\Enums\ObjectIcon; use Chamilo\CoreBundle\Framework\Container; use Chamilo\CoreBundle\Helpers\ChamiloHelper; use Chamilo\CoreBundle\Helpers\ContainerHelper; @@ -20,13 +21,28 @@ * This script displays a form for registering new users. */ -//quick hack to adapt the registration form result to the selected registration language +// Quick hack to adapt the registration form result to the selected registration language. if (!empty($_POST['language'])) { $_GET['language'] = $_POST['language']; } $hideHeaders = isset($_GET['hide_headers']); +/** + * Registration settings (backward-compatible). + * Note: We ALWAYS show the role selector (UI requirement), + * but we still enforce platform rules (security) by disabling teacher role + * when the setting is disabled, and forcing STUDENT on submit. + */ +$allowTeacherRegistrationRaw = api_get_setting('registration.allow_registration_as_teacher'); +if (null === $allowTeacherRegistrationRaw || '' === (string) $allowTeacherRegistrationRaw) { + $allowTeacherRegistrationRaw = api_get_setting('allow_registration_as_teacher'); +} +$allowTeacherRegistration = \in_array((string) $allowTeacherRegistrationRaw, ['true', '1', 'yes'], true); + +// Expose to JS (for UI disabling). +$htmlHeadXtra[] = ''; + $allowedFields = [ 'official_code', 'phone', @@ -42,105 +58,175 @@ $allowedFields['extra_fields'] = $allowedFieldsConfiguration['extra_fields'] ?? []; } +// UI requirement: always show role selector even if config removes it. +if (!in_array('status', $allowedFields, true)) { + $allowedFields[] = 'status'; +} + $pluginTccDirectoryPath = api_get_path(SYS_PLUGIN_PATH) . 'logintcc'; $isTccEnabled = (is_dir($pluginTccDirectoryPath) && Container::getPluginHelper()->isPluginEnabled('logintcc')); $webserviceUrl = ''; $hash = ''; if ($isTccEnabled) { - // Configure TCC plugin settings and JavaScript for the form - // (This section includes the JavaScript code for the TCC plugin integration) - $webserviceUrl = api_get_plugin_setting('logintcc', 'webservice_url'); - $hash = api_get_plugin_setting('logintcc', 'hash'); - $htmlHeadXtra[] = ''; +}); + +EOD; } $extraFieldsLoaded = false; $htmlHeadXtra[] = api_get_password_checker_js('#username', '#pass1'); -$registeringText = addslashes(get_lang('Registering')); + +// Avoid JS syntax errors with translations (apostrophes, newlines, etc.). +$registeringTextJs = json_encode((string) get_lang('Registering'), JSON_UNESCAPED_UNICODE); + $htmlHeadXtra[] = << document.addEventListener('DOMContentLoaded', function() { const form = document.querySelector('form[name="registration"]'); - if (form) { - form.addEventListener('submit', function(event) { - const submitButtons = form.querySelectorAll('button[type="submit"], input[type="submit"]'); - submitButtons.forEach(btn => { - btn.disabled = true; - btn.classList.add('disabled'); - btn.innerText = '{$registeringText}'; - }); + if (!form) return; + + const registeringText = {$registeringTextJs}; + + form.addEventListener('submit', function() { + const submitButtons = form.querySelectorAll('button[type="submit"], input[type="submit"]'); + submitButtons.forEach(btn => { + btn.disabled = true; + btn.classList.add('disabled'); + if (btn.tagName === 'BUTTON') { + btn.innerText = registeringText; + } else { + btn.value = registeringText; + } }); - } + }); }); EOD; +/** + * Tailwind-only UI polish for legacy QuickForm output (non-invasive). + * This keeps existing functionality and only enhances classes at runtime. + */ +$htmlHeadXtra[] = << +document.addEventListener('DOMContentLoaded', function () { + const form = document.querySelector('form[name="registration"]'); + if (!form) return; + + // Form container + form.classList.add('max-w-4xl','mx-auto','bg-white','rounded-2xl','border','border-gray-25','shadow-sm','p-6','md:p-8'); + + // Inputs / selects / textareas + const controls = form.querySelectorAll('input[type="text"], input[type="email"], input[type="password"], input[type="tel"], select, textarea'); + controls.forEach(el => { + // Skip hidden fields + if (el.type === 'hidden') return; + + el.classList.add( + 'w-full','rounded-xl','border','border-gray-25','bg-white', + 'px-4','py-3','text-gray-90', + 'focus:outline-none','focus:ring-2','focus:ring-gray-60','focus:border-gray-60' + ); + }); + + // Labels + form.querySelectorAll('label').forEach(l => { + l.classList.add('text-sm','font-medium','text-gray-90'); + }); + + // Required markers often appear as * + form.querySelectorAll('.form_required').forEach(s => { + s.classList.add('text-red-60','font-semibold'); + }); + + // Error messages + form.querySelectorAll('.form_error, .error').forEach(err => { + err.classList.add('text-red-60','text-sm','mt-1'); + }); -// User is not allowed if Terms and Conditions are disabled and -// registration is disabled too. + // Improve spacing between rows (best-effort) + form.querySelectorAll('tr').forEach(tr => tr.classList.add('align-top')); +}); + +EOD; + +// User is not allowed if Terms and Conditions are disabled and registration is disabled too. $isCreatingIntroPage = isset($_GET['create_intro_page']); $isPlatformAdmin = api_is_platform_admin(); @@ -159,9 +245,12 @@ $settingConditions = api_get_setting('profile.show_conditions_to_user', true); $extraConditions = 'false' !== $settingConditions ? $settingConditions : []; if ($extraConditions && isset($extraConditions['conditions'])) { - // Create user extra fields for the conditions + // Create user extra fields for the conditions. $userExtraField = new ExtraField('user'); + + // Use the resolved array to avoid relying on $settingConditions type. $extraConditions = $extraConditions['conditions']; + foreach ($extraConditions as $condition) { $exists = $userExtraField->get_handler_field_info_by_field_variable($condition['variable']); if (false == $exists) { @@ -186,7 +275,7 @@ if ('true' === api_get_setting('allow_terms_conditions')) { $userAlreadyRegisteredShowTerms = isset($termRegistered['user_id']); // Ofaj change - if (true === api_is_anonymous() && 'course' === api_get_setting('workflows.load_term_conditions_section')) { + if (true === api_is_anonymous() && 'course' === api_get_setting('workflows.load_term_conditions_section')) { $userAlreadyRegisteredShowTerms = false; } } @@ -225,9 +314,239 @@ } // allow_registration can be 'true', 'false', 'approval' or 'confirmation'. Only 'false' hides the form. -if (false === $userAlreadyRegisteredShowTerms && - 'false' !== api_get_setting('allow_registration') -) { +if (false === $userAlreadyRegisteredShowTerms && 'false' !== api_get_setting('allow_registration')) { + /** + * ROLE SELECTOR (Learner / Teacher) + * UI: always shown (forced in allowed fields). + * Security: teacher option disabled if platform forbids it, and backend forces STUDENT on submit. + */ + if (in_array('status', $allowedFields, true)) { + $iconSize = defined('ICON_SIZE_MEDIUM') ? ICON_SIZE_MEDIUM : 28; + + $renderIcon = static function (ObjectIcon|string $icon) use ($iconSize): string { + $iconName = $icon instanceof ObjectIcon ? $icon->value : $icon; + + if (method_exists(Display::class, 'getMdiIcon')) { + return Display::getMdiIcon($iconName, 'text-gray-70', null, $iconSize); + } + + return ''; + }; + + $studentIcon = $renderIcon(ObjectIcon::USER); + $teacherIcon = $renderIcon(ObjectIcon::TEACHER); + + $title = get_lang('What do you want to do?'); + $notAvailable = get_lang('Not available'); + + $studentValue = (int) STUDENT; + $teacherValue = (int) COURSEMANAGER; + + $teacherValueJs = json_encode((string) $teacherValue, JSON_UNESCAPED_UNICODE); + + // Title (with required asterisk) above cards. + $form->addHtml(' +
+
'.$title.' *
+
+ '); + + // Render our own cards (stable grid), and keep QuickForm radios hidden for validation/submission. + $teacherHint = $allowTeacherRegistration ? '' : $notAvailable; + $teacherDisabledAttr = $allowTeacherRegistration ? '' : ' disabled aria-disabled="true"'; + + $form->addHtml(' +
+ + + +
+ '); + + // Keep QuickForm group (required rule + real field). We'll hide it with CSS. + $form->addRadio( + 'status', + null, + [ + $studentValue => get_lang('Follow courses'), + $teacherValue => get_lang('Teach courses'), + ], + ['class' => 'register-profile'] + ); + + $form->addRule('status', get_lang('Required field'), 'required'); + + $htmlHeadXtra[] = << +/* Card grid: 1 column on small screens, 2 columns on md+ */ +.role-cards-grid{ + display:grid; + grid-template-columns: 1fr; + gap: 1rem; +} +@media (min-width: 768px){ + .role-cards-grid{ + grid-template-columns: repeat(2, minmax(0, 1fr)); + } +} + +/* Card styling */ +.role-card{ + width:100%; + display:flex; + align-items:center; + gap:1rem; + padding:1rem; + border:1px solid #E5E7EB; + border-radius:1rem; + background:#fff; + box-shadow: 0 1px 2px rgba(0,0,0,.05); + cursor:pointer; + text-align:left; + transition: background .15s ease, border-color .15s ease, box-shadow .15s ease; +} +.role-card:hover{ background:#F9FAFB; } +.role-card:disabled{ opacity:.5; cursor:not-allowed; } +.role-card__icon{ + width:48px; height:48px; + border-radius:12px; + background:#F3F4F6; + display:flex; + align-items:center; + justify-content:center; + flex:0 0 48px; +} +.role-card__text{ display:flex; flex-direction:column; gap:.125rem; } +.role-card__title{ font-weight:600; color:#111827; } +.role-card__subtitle{ font-size:.875rem; color:#6B7280; } + +.role-card.is-selected{ + border-color:#6B7280; + box-shadow: 0 0 0 2px rgba(107,114,128,.35); + background:#F3F4F6; +} + +/* Hard hide: remove the whole native QuickForm row that contains status radios (Chrome supports :has) */ +form[name="registration"] tr:has(input[type="radio"][name="status"]), +form[name="registration"] tr:has(input[type="radio"][name^="status["]), +form[name="registration"] li:has(input[type="radio"][name="status"]), +form[name="registration"] li:has(input[type="radio"][name^="status["]){ + display:none !important; +} + + + +EOD; + } + // EMAIL $form->addElement('text', 'email', get_lang('E-mail'), ['size' => 40]); if ('true' === api_get_setting('registration', 'email')) { @@ -238,22 +557,6 @@ $form->addButtonSearch(get_lang('SearchTCC'), 'search', ['id' => 'search_user']); } - // STUDENT/TEACHER - if ('false' != api_get_setting('allow_registration_as_teacher')) { - if (in_array('status', $allowedFields)) { - $form->addRadio( - 'status', - get_lang('What do you want to do?'), - [ - STUDENT => '

'.get_lang('Follow courses').'

', - COURSEMANAGER => '

'.get_lang('Teach courses').'

', - ], - ['class' => 'register-profile'] - ); - $form->addRule('status', get_lang('Required field'), 'required'); - } - } - $LastnameLabel = get_lang('Last name'); if ('true' === api_get_setting('profile.registration_add_helptext_for_2_names')) { $LastnameLabel = [$LastnameLabel, get_lang('Insert your two names')]; @@ -364,55 +667,25 @@ function ($email) { } // PHONE - if (in_array('phone', $allowedFields)) { - $form->addElement( - 'text', - 'phone', - get_lang('Phone'), - ['size' => 20] - ); - $form->addRule( - 'phone', - get_lang('Required field'), - 'required' - ); + if (in_array('phone', $allowedFields, true)) { + $form->addElement('text', 'phone', get_lang('Phone'), ['size' => 20]); + $form->addRule('phone', get_lang('Required field'), 'required'); } // Language - if (in_array('language', $allowedFields)) { - $form->addSelectLanguage( - 'language', - get_lang('Language'), - [], - ['id' => 'language'] - ); + if (in_array('language', $allowedFields, true)) { + $form->addSelectLanguage('language', get_lang('Language'), [], ['id' => 'language']); } - if (in_array('official_code', $allowedFields)) { - $form->addElement( - 'text', - 'official_code', - get_lang('Official code'), - ['size' => 40] - ); - $form->addRule( - 'official_code', - get_lang('Required field'), - 'required' - ); + if (in_array('official_code', $allowedFields, true)) { + $form->addElement('text', 'official_code', get_lang('Official code'), ['size' => 40]); + $form->addRule('official_code', get_lang('Required field'), 'required'); } if (in_array('date_of_birth', $allowedFields, true)) { - $form->addDatePicker( - 'date_of_birth', - get_lang('Date of birth'), - ['required' => false] - ); + $form->addDatePicker('date_of_birth', get_lang('Date of birth'), ['required' => false]); } - $captcha = api_get_setting('allow_captcha'); - $allowCaptcha = 'true' === $captcha; - // EXTENDED FIELDS if ('true' === api_get_setting('extended_profile') && 'true' === api_get_setting('extendedprofile_registration', 'mycomptetences') @@ -485,20 +758,11 @@ function ($email) { } } - $form->addElement( - 'hidden', - 'extra_tcc_user_id' - ); - - $form->addElement( - 'hidden', - 'extra_tcc_hash_key' - ); + $form->addElement('hidden', 'extra_tcc_user_id'); + $form->addElement('hidden', 'extra_tcc_hash_key'); // EXTRA FIELDS - if (array_key_exists('extra_fields', $allowedFields) || - in_array('extra_fields', $allowedFields) - ) { + if (array_key_exists('extra_fields', $allowedFields) || in_array('extra_fields', $allowedFields, true)) { $extraField = new ExtraField('user'); $extraFieldList = []; if (isset($allowedFields['extra_fields']) && is_array($allowedFields['extra_fields'])) { @@ -511,7 +775,7 @@ function ($email) { $requiredFields = $requiredFields['options']; } - $returnParams = $extraField->addElements( + $extraField->addElements( $form, 0, [], @@ -547,37 +811,15 @@ function ($email) { 'font_size' => 20, 'font_path' => api_get_path(SYS_FONTS_PATH).'opensans/', 'font_file' => 'OpenSans-Regular.ttf', - //'output' => 'gif' ], ]; - $captcha_question = $form->addElement( - 'CAPTCHA_Image', - 'captcha_question', - '', - $options - ); + $captcha_question = $form->addElement('CAPTCHA_Image', 'captcha_question', '', $options); $form->addElement('static', null, null, get_lang('Click on the image to load a new one.')); - $form->addElement( - 'text', - 'captcha', - get_lang('Enter the letters you see.'), - ['size' => 40] - ); - $form->addRule( - 'captcha', - get_lang('Enter the characters you see on the image'), - 'required', - null, - 'client' - ); - $form->addRule( - 'captcha', - get_lang('The text you entered doesn\'t match the picture.'), - 'CAPTCHA', - $captcha_question - ); + $form->addElement('text', 'captcha', get_lang('Enter the letters you see.'), ['size' => 40]); + $form->addRule('captcha', get_lang('Enter the characters you see on the image'), 'required', null, 'client'); + $form->addRule('captcha', get_lang('The text you entered doesn\'t match the picture.'), 'CAPTCHA', $captcha_question); } } @@ -596,11 +838,9 @@ function ($email) { if (!empty($_GET['email'])) { $defaults['email'] = Security::remove_XSS($_GET['email']); } - if (!empty($_GET['phone'])) { $defaults['phone'] = Security::remove_XSS($_GET['phone']); } - if ('true' === api_get_setting('openid_authentication') && !empty($_GET['openid'])) { $defaults['openid'] = Security::remove_XSS($_GET['openid']); } @@ -625,22 +865,14 @@ function ($email) { $content .= Display::return_message(get_lang('Your account has to be approved')); } -//if openid was not found +// if openid was not found if (!empty($_GET['openid_msg']) && 'idnotfound' == $_GET['openid_msg']) { - $content .= Display::return_message(get_lang('This OpenID could not be found in our database. Please register for a new account. If you have already an account with us, please edit your profile inside your account to add this OpenID')); + $content .= Display::return_message(get_lang('This OpenID could not be found in our database. Please register for a new account. If you already an account with us, please edit your profile inside your account to add this OpenID')); } if ($extraConditions) { - $form->addCheckBox( - 'extra_platformuseconditions', - null, - get_lang('Platform use conditions') - ); - $form->addRule( - 'extra_platformuseconditions', - get_lang('Required field'), - 'required' - ); + $form->addCheckBox('extra_platformuseconditions', null, get_lang('Platform use conditions')); + $form->addRule('extra_platformuseconditions', get_lang('Required field'), 'required'); } $blockButton = false; @@ -677,17 +909,10 @@ function ($email) { $form->addLabel( null, - Display::url( - get_lang('Validate'), - 'javascript:void', - ['class' => 'btn btn--plain', 'id' => 'pre_validation'] - ) + Display::url(get_lang('Validate'), 'javascript:void', ['class' => 'btn btn--plain', 'id' => 'pre_validation']) ); $form->addHtml(''); } else { @@ -737,7 +962,8 @@ function ($email) { $values['username'] = api_substr($values['username'], 0, User::USERNAME_MAX_LENGTH); } - if ('false' === api_get_setting('allow_registration_as_teacher')) { + // Security rule: if teacher registration is disabled, force learner status. + if (!$allowTeacherRegistration) { $values['status'] = STUDENT; } @@ -749,13 +975,11 @@ function ($email) { $values['username'] = $values['email']; } - // Moved here to include extra fields when creating a user. Formerly placed after user creation // Register extra fields $extras = []; $extraParams = []; foreach ($values as $key => $value) { if ('extra_' === substr($key, 0, 6)) { - //an extra field $extras[substr($key, 6)] = $value; $extraParams[$key] = $value; } @@ -766,7 +990,7 @@ function ($email) { $values['language'] = isset($values['language']) ? $values['language'] : api_get_language_isocode(); $values['address'] = $values['address'] ?? ''; - // It gets a creator id when user is not logged + // It gets a creator id when user is not logged. $creatorId = 0; if (api_is_anonymous()) { $adminList = UserManager::get_all_administrators(); @@ -803,10 +1027,8 @@ function ($email) { $creatorId ); - // save T&C acceptance - if ('true' === api_get_setting('allow_terms_conditions') - && !empty($values['legal_accept_type']) - ) { + // Save T&C acceptance + if ('true' === api_get_setting('allow_terms_conditions') && !empty($values['legal_accept_type'])) { ChamiloHelper::saveUserTermsAcceptance($userId, $values['legal_accept_type']); } @@ -814,11 +1036,9 @@ function ($email) { $countExtraField = count($extras); if ($countExtraField > 0 && is_int($userId)) { foreach ($extras as $key => $value) { - // For array $value -> if exists key 'tmp_name' then must not be empty - // This avoid delete from user field value table when doesn't upload a file if (is_array($value)) { if (array_key_exists('tmp_name', $value) && empty($value['tmp_name'])) { - //Nothing to do + // Nothing to do. } else { if (array_key_exists('tmp_name', $value)) { $value['tmp_name'] = Security::filter_filename($value['tmp_name']); @@ -839,30 +1059,22 @@ function ($email) { $store_extended = false; $sql = "UPDATE ".Database::get_main_table(TABLE_MAIN_USER)." SET "; - if ('true' == api_get_setting('extended_profile') && - 'true' == api_get_setting('extendedprofile_registration', 'mycomptetences') - ) { + if ('true' == api_get_setting('extended_profile') && 'true' == api_get_setting('extendedprofile_registration', 'mycomptetences')) { $sql_set[] = "competences = '".Database::escape_string($values['competences'])."'"; $store_extended = true; } - if ('true' == api_get_setting('extended_profile') && - 'true' == api_get_setting('extendedprofile_registration', 'mydiplomas') - ) { + if ('true' == api_get_setting('extended_profile') && 'true' == api_get_setting('extendedprofile_registration', 'mydiplomas')) { $sql_set[] = "diplomas = '".Database::escape_string($values['diplomas'])."'"; $store_extended = true; } - if ('true' == api_get_setting('extended_profile') && - 'true' == api_get_setting('extendedprofile_registration', 'myteach') - ) { + if ('true' == api_get_setting('extended_profile') && 'true' == api_get_setting('extendedprofile_registration', 'myteach')) { $sql_set[] = "teach = '".Database::escape_string($values['teach'])."'"; $store_extended = true; } - if ('true' == api_get_setting('extended_profile') && - 'true' == api_get_setting('extendedprofile_registration', 'mypersonalopenarea') - ) { + if ('true' == api_get_setting('extended_profile') && 'true' == api_get_setting('extendedprofile_registration', 'mypersonalopenarea')) { $sql_set[] = "openarea = '".Database::escape_string($values['openarea'])."'"; $store_extended = true; } @@ -986,19 +1198,15 @@ function ($email) { 'lastName' => stripslashes($values['lastname']), 'mail' => $values['email'], 'language' => $values['language'], - 'user_id' => $userId + 'user_id' => $userId, ]; $sessionHandler->set('_user', $userData); $sessionHandler->set('_locale_user', $userEntity->getLocale()); - $is_allowedCreateCourse = isset($values['status']) && 1 == $values['status']; $sessionHandler->set('is_allowedCreateCourse', $is_allowedCreateCourse); // Stats - Container::getTrackELoginRepository() - ->createLoginRecord($userEntity, new DateTime(), $request->getClientIp()) - ; - // @todo implement Auto-subscribe according to STATUS_autosubscribe setting + Container::getTrackELoginRepository()->createLoginRecord($userEntity, new DateTime(), $request->getClientIp()); // last user login date is now $user_last_login_datetime = 0; // used as a unix timestamp it will correspond to : 1 1 1970 @@ -1191,7 +1399,8 @@ function ($email) { } if ($introPage) { - $content = '
' + // Tailwind-styled info box (keeps content intact). + $content = '
' . $introPage->getContent() . '
' . $content; } diff --git a/public/main/inc/lib/message.lib.php b/public/main/inc/lib/message.lib.php index d1dd401e5cb..ff40dbce903 100644 --- a/public/main/inc/lib/message.lib.php +++ b/public/main/inc/lib/message.lib.php @@ -133,7 +133,8 @@ public static function send_message( $checkCurrentAudioId = false, $forceTitleWhenSendingEmail = false, $msgType = null, - $baseUrl = null + $baseUrl = null, + $sendEmailNotification = true ) { $group_id = (int) $group_id; $receiverUserId = (int) $receiverUserId; @@ -164,9 +165,15 @@ public static function send_message( return false; } + // Email notifications are enabled by default, but can be explicitly disabled (e.g., inbox copy after registration). $sendEmail = true; - // Disabling messages depending the pausetraining plugin. + if (false === (bool) $sendEmailNotification) { + $sendEmail = false; + } + + // Disabling messages depending the pausetraining plugin (only relevant if email notifications are enabled). $allowPauseFormation = + $sendEmail && Container::getPluginHelper()->isPluginEnabled('PauseTraining') && 'true' === api_get_plugin_setting('PauseTraining', 'allow_users_to_edit_pause_formation'); @@ -322,7 +329,7 @@ public static function send_message( // Save attachment file for inbox messages if (is_array($attachmentList)) { foreach ($attachmentList as $attachment) { - if (0 === $attachment['error']) { + if (0 === ($attachment['error'] ?? 0)) { self::saveMessageAttachmentFile( $attachment, $attachment['comment'] ?? '', @@ -349,18 +356,19 @@ public static function send_message( $em->flush(); } + // Email notification (optional) if ($sendEmail) { $notification = new Notification(); $sender_info = api_get_user_info($user_sender_id); $baseUrl = $baseUrl ?? api_get_path(WEB_PATH); - $content = self::processRelativeLinks($content, $baseUrl); + $contentForEmail = self::processRelativeLinks($content, $baseUrl); - // add file attachment additional attributes + // Add file attachment additional attributes $attachmentAddedByMail = []; foreach ($attachmentList as $attachment) { $attachmentAddedByMail[] = [ - 'path' => $attachment['tmp_name'], - 'filename' => $attachment['name'], + 'path' => $attachment['tmp_name'] ?? '', + 'filename' => $attachment['name'] ?? '', ]; } @@ -374,7 +382,7 @@ public static function send_message( $type, [$receiverUserId], $subject, - $content, + $contentForEmail, $sender_info, $attachmentAddedByMail, $forceTitleWhenSendingEmail, @@ -394,23 +402,25 @@ public static function send_message( 1000 ); - // Adding more sense to the message group - $subject = sprintf(get_lang('There is a new message in group %s'), $group_info['title']); + // Add more context for group message subject + $subjectGroup = sprintf(get_lang('There is a new message in group %s'), $group_info['title']); $new_user_list = []; foreach ($user_list as $user_data) { $new_user_list[] = $user_data['id']; } - $group_info = [ + + $groupPayload = [ 'group_info' => $group_info, 'user_info' => $sender_info, ]; + $notification->saveNotification( $messageId, Notification::NOTIFICATION_TYPE_GROUP, $new_user_list, - $subject, - $content, - $group_info, + $subjectGroup, + $contentForEmail, + $groupPayload, $attachmentAddedByMail, $forceTitleWhenSendingEmail, $baseUrl @@ -458,7 +468,8 @@ public static function send_message_simple( $sendCopyToDrhUsers = false, $directMessage = false, $uploadFiles = true, - $attachmentList = [] + $attachmentList = [], + $sendEmailNotification = true ) { $files = $_FILES ? $_FILES : []; if (false === $uploadFiles) { @@ -479,7 +490,13 @@ public static function send_message_simple( null, null, $sender_id, - $directMessage + $directMessage, + 0, + false, + false, + null, + null, + $sendEmailNotification ); if ($sendCopyToDrhUsers) { @@ -488,9 +505,9 @@ public static function send_message_simple( if (!empty($drhList)) { foreach ($drhList as $drhInfo) { $message = sprintf( - get_lang('Copy of message sent to %s'), - $userInfo['complete_name'] - ).'
'.$message; + get_lang('Copy of message sent to %s'), + $userInfo['complete_name'] + ).'
'.$message; self::send_message_simple( $drhInfo['id'], diff --git a/public/main/inc/lib/usermanager.lib.php b/public/main/inc/lib/usermanager.lib.php index a21b30b22b7..25d4acfbc91 100644 --- a/public/main/inc/lib/usermanager.lib.php +++ b/public/main/inc/lib/usermanager.lib.php @@ -211,9 +211,7 @@ public static function create_user( if (in_array(UserAuthSource::PLATFORM, $authSources)) { Display::addFlash( Display::return_message( - get_lang('Required field').': '.get_lang( - 'Password' - ), + get_lang('Required field').': '.get_lang('Password'), 'warning' ) ); @@ -299,9 +297,7 @@ public static function create_user( if (is_array($extra) && count($extra) > 0) { $extra['item_id'] = $userId; $userFieldValue = new ExtraFieldValue('user'); - /* Force saving of extra fields (otherwise, if the current - user is not admin, fields not visible to the user - most - of them - are just ignored) */ + /* Force saving of extra fields (otherwise fields not visible are ignored) */ $userFieldValue->saveFieldValues( $extra, true, @@ -312,28 +308,12 @@ public static function create_user( ); } else { // Create notify settings by default - self::update_extra_field_value( - $userId, - 'mail_notify_invitation', - '1' - ); - self::update_extra_field_value( - $userId, - 'mail_notify_message', - '1' - ); - self::update_extra_field_value( - $userId, - 'mail_notify_group_message', - '1' - ); + self::update_extra_field_value($userId, 'mail_notify_invitation', '1'); + self::update_extra_field_value($userId, 'mail_notify_message', '1'); + self::update_extra_field_value($userId, 'mail_notify_group_message', '1'); } - self::update_extra_field_value( - $userId, - 'already_logged_in', - 'false' - ); + self::update_extra_field_value($userId, 'already_logged_in', 'false'); if (!empty($redirectToURLAfterLogin) && ('true' === api_get_setting('workflows.plugin_redirection_enabled'))) { RedirectionPlugin::insert($userId, $redirectToURLAfterLogin); @@ -347,7 +327,12 @@ public static function create_user( PERSON_NAME_EMAIL_ADDRESS ); $tpl = Container::getTwig(); - $emailSubject = $tpl->render('@ChamiloCore/Mailer/Legacy/subject_registration_platform.html.twig', ['locale' => $userLocale]); + + $emailSubject = $tpl->render( + '@ChamiloCore/Mailer/Legacy/subject_registration_platform.html.twig', + ['locale' => $userLocale] + ); + $sender_name = api_get_person_name( api_get_setting('administratorName'), api_get_setting('administratorSurname'), @@ -367,32 +352,35 @@ public static function create_user( } } - // variables for the default template + // Variables for the default template $params = [ 'complete_name' => stripslashes(api_get_person_name($firstName, $lastName)), 'login_name' => $loginName, - 'original_password' => stripslashes($original_password), + 'original_password' => stripslashes((string) $original_password), 'mailWebPath' => $url, 'new_user' => $user, 'search_link' => $url, 'locale' => $userLocale, ]; - // ofaj if ('true' === api_get_setting('session.allow_search_diagnostic')) { $urlSearch = api_get_path(WEB_CODE_PATH).'search/search.php'; $linkSearch = Display::url($urlSearch, $urlSearch); $params['search_link'] = $linkSearch; } - $emailBody = $tpl->render( + // Default Twig bodies: one for email (with password) and one for inbox (without password) + $emailBodyEmail = $tpl->render( '@ChamiloCore/Mailer/Legacy/content_registration_platform.html.twig', - $params + $params + ['show_password' => true] + ); + $emailBodyInbox = $tpl->render( + '@ChamiloCore/Mailer/Legacy/content_registration_platform.html.twig', + $params + ['show_password' => false] ); $userInfo = api_get_user_info($userId); $mailTemplateManager = new MailTemplateManager(); - $phoneNumber = $extra['mobile_phone_number'] ?? null; $emailBodyTemplate = ''; if (!empty($emailTemplate)) { @@ -406,8 +394,15 @@ public static function create_user( } } + // If a custom email template is provided, use it only for the email (inbox copy stays sanitized Twig) + if (!empty($emailBodyTemplate)) { + $emailBodyEmail = $emailBodyTemplate; + } + $twoEmail = ('true' === api_get_setting('mail.send_two_inscription_confirmation_mail')); + if (true === $twoEmail) { + // Keep existing 2-email behavior (no structural changes) $emailBody = $tpl->render('@ChamiloCore/Mailer/Legacy/new_user_first_email_confirmation.html.twig'); if (!empty($emailBodyTemplate) && isset($emailTemplate['new_user_first_email_confirmation.tpl']) && @@ -457,10 +452,8 @@ public static function create_user( [], $creatorEmail ); - } else { - if (!empty($emailBodyTemplate)) { - $emailBody = $emailBodyTemplate; - } + + // Optional inbox copy (sanitized, and no email notification) $sendToInbox = ('true' === api_get_setting('registration.send_inscription_msg_to_inbox')); if ($sendToInbox) { $adminList = self::get_all_administrators(); @@ -473,32 +466,68 @@ public static function create_user( MessageManager::send_message_simple( $userId, $emailSubject, - $emailBody, - $senderId + $emailBodyInbox, + $senderId, + false, + false, + false, + [], + false ); - } else { - api_mail_html( - $recipient_name, - $email, + } + } else { + // 1) Always send the registration email + api_mail_html( + $recipient_name, + $email, + $emailSubject, + $emailBodyEmail, + $sender_name, + $email_admin, + [], + [], + false, + [], + $creatorEmail + ); + + // 2) Optionally copy to Chamilo inbox (sanitized, no email notification) + $sendToInbox = ('true' === api_get_setting('registration.send_inscription_msg_to_inbox')); + if ($sendToInbox) { + $adminList = self::get_all_administrators(); + $senderId = 1; + if (!empty($adminList)) { + $adminInfo = current($adminList); + $senderId = $adminInfo['user_id']; + } + + MessageManager::send_message_simple( + $userId, $emailSubject, - $emailBody, - $sender_name, - $email_admin, - [], - [], + $emailBodyInbox, + $senderId, + false, + false, false, [], - $creatorEmail + false ); } } + // Admin notifications (keep behavior; use the email version as the reference body) $notification = api_get_setting('profile.send_notification_when_user_added', true); if (!empty($notification) && isset($notification['admins']) && is_array($notification['admins'])) { foreach ($notification['admins'] as $adminId) { $emailSubjectToAdmin = get_lang('The user has been added').': '. api_get_person_name($firstName, $lastName); - MessageManager::send_message_simple($adminId, $emailSubjectToAdmin, $emailBody, $userId); + + MessageManager::send_message_simple( + $adminId, + $emailSubjectToAdmin, + $emailBodyEmail, + $userId + ); } } diff --git a/public/main/template/default/auth/inscription.html.twig b/public/main/template/default/auth/inscription.html.twig index a34b64b0c56..5fcfb6f5d33 100644 --- a/public/main/template/default/auth/inscription.html.twig +++ b/public/main/template/default/auth/inscription.html.twig @@ -5,9 +5,9 @@ %} {% block content %} - {# full‐height viewport, centered #} -
- {# full‐width container up to large screen #} + {% set isPostRegistration = text_after_registration is not empty %} + +
{# header if present #} @@ -17,6 +17,13 @@
{% endif %} + {# post-registration message should be visible at the top #} + {% if text_after_registration %} +
+ {{ text_after_registration|raw }} +
+ {% endif %} + {# intro text or notices #} {% if inscription_content %}
@@ -24,15 +31,10 @@
{% endif %} - {# registration form takes full width #} -
- {{ form|raw }} -
- - {# post‐registration message #} - {% if text_after_registration %} -
- {{ text_after_registration|raw }} + {# registration form takes full width (only if present) #} + {% if form %} +
+ {{ form|raw }}
{% endif %} diff --git a/src/CoreBundle/Resources/views/Mailer/Legacy/content_registration_platform.html.twig b/src/CoreBundle/Resources/views/Mailer/Legacy/content_registration_platform.html.twig index 86fa84ea5f6..92544ad9153 100644 --- a/src/CoreBundle/Resources/views/Mailer/Legacy/content_registration_platform.html.twig +++ b/src/CoreBundle/Resources/views/Mailer/Legacy/content_registration_platform.html.twig @@ -6,7 +6,10 @@

{{ 'Welcome to this platform'|trans({}, 'messages', user_locale) }}

{{ 'You are registered to'|trans({}, 'messages', user_locale) }} {{ 'platform.site_name' | api_get_setting }} {{ 'with the following settings:'|trans({}, 'messages', user_locale) }}

{{ 'Username'|trans({}, 'messages', user_locale) }} : {{ login_name }}
- {{ 'Pass'|trans({}, 'messages', user_locale) }} : {{ original_password }}

+ {% if show_password is not defined or show_password %} + {{ 'Pass'|trans({}, 'messages', user_locale) }} : {{ original_password }} + {% endif %} +

{{ 'For more details visit %s'|trans({'%s': search_link}, 'messages', user_locale) }}

{{ 'In case of trouble, contact us.'|trans({}, 'messages', user_locale) }}