Skip to content

Commit 03b4368

Browse files
Internal: Improve Terms and Conditions handling - refs BT#22774
1 parent c761a6d commit 03b4368

File tree

7 files changed

+156
-29
lines changed

7 files changed

+156
-29
lines changed

assets/vue/composables/auth/login.js

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -29,31 +29,29 @@ export function useLogin() {
2929
requires2FA.value = false
3030

3131
try {
32-
// Build the payload using OFAJ style
32+
// Prepare payload as expected by securityService
3333
const payload = {
34-
username: login,
34+
login,
3535
password,
3636
_remember_me,
37+
totp,
3738
}
3839

39-
if (totp) {
40-
payload.totp = totp
41-
}
42-
40+
// Add returnUrl if exists in query param
4341
const returnUrl = route.query.redirect?.toString() || null
4442
if (returnUrl) {
4543
payload.returnUrl = returnUrl
4644
}
4745

4846
const responseData = await securityService.login(payload)
4947

50-
// Handle 2FA
48+
// Handle 2FA flow
5149
if (responseData.requires2FA && !payload.totp) {
5250
requires2FA.value = true
5351
return { success: false, requires2FA: true }
5452
}
5553

56-
// Handle rotate password flow
54+
// Handle forced password rotation
5755
if (responseData.rotate_password && responseData.redirect) {
5856
window.location.href = responseData.redirect
5957
return { success: true, rotate: true }
@@ -65,13 +63,13 @@ export function useLogin() {
6563
return { success: false, error: responseData.error }
6664
}
6765

68-
// Handle terms and conditions redirection
66+
// Handle terms and conditions redirect
6967
if (responseData.load_terms && responseData.redirect) {
7068
window.location.href = responseData.redirect
7169
return { success: true, redirect: responseData.redirect }
7270
}
7371

74-
// External redirect parameter
72+
// Handle external redirect param
7573
if (route.query.redirect) {
7674
const redirectParam = route.query.redirect.toString()
7775
if (isValidHttpUrl(redirectParam)) {
@@ -88,16 +86,17 @@ export function useLogin() {
8886
return { success: true }
8987
}
9088

89+
// Save user info
9190
securityStore.setUser(responseData)
9291
await platformConfigurationStore.initialize()
9392

94-
// Handle redirect param again after login
93+
// Redirect again if redirect param still exists
9594
if (route.query.redirect) {
9695
await router.replace({ path: route.query.redirect.toString() })
9796
return { success: true }
9897
}
9998

100-
// Handle post-login redirection based on platform config
99+
// Default platform redirect after login
101100
const setting = platformConfigurationStore.getSetting("registration.redirect_after_login")
102101
let target = "/"
103102

public/main/auth/tc.php

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,53 @@
11
<?php
22
require_once __DIR__.'/../inc/global.inc.php';
33

4+
use Chamilo\CoreBundle\Framework\Container;
45
use Chamilo\CoreBundle\Helpers\ChamiloHelper;
6+
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
57
use ChamiloSession as Session;
68

79
$return = $_POST['return'] ?? $_GET['return'] ?? 'index.php';
810

911
if ($_SERVER['REQUEST_METHOD'] === 'POST'
1012
&& !empty($_POST['legal_accept_type'])
1113
&& isset($_POST['legal_accept'])
12-
&& ($uid = api_get_user_id())
1314
) {
14-
ChamiloHelper::saveUserTermsAcceptance($uid, $_POST['legal_accept_type']);
15+
$userId = 0;
16+
$termData = Session::read('term_and_condition');
17+
if (!empty($termData['user_id'])) {
18+
$userId = (int)$termData['user_id'];
19+
} else {
20+
$userId = api_get_user_id();
21+
}
1522

16-
Session::write('term_and_condition', null);
23+
if ($userId > 0) {
24+
ChamiloHelper::saveUserTermsAcceptance($userId, $_POST['legal_accept_type']);
1725

18-
ChamiloHelper::redirectTo($return);
26+
// Re-login in Symfony security
27+
$userEntity = api_get_user_entity($userId);
28+
if ($userEntity) {
29+
$token = new UsernamePasswordToken(
30+
$userEntity,
31+
'main',
32+
$userEntity->getRoles()
33+
);
34+
35+
$tokenStorage = Container::getTokenStorage();
36+
$tokenStorage->setToken($token);
37+
38+
// Save the token to session so the firewall recognizes it on the next request
39+
$session = Container::getSession();
40+
if ($session) {
41+
$session->set('_security_main', serialize($token));
42+
}
43+
}
44+
45+
Session::write('term_and_condition', null);
46+
47+
ChamiloHelper::redirectTo($return);
48+
} else {
49+
die('Error: Unable to identify user accepting terms.');
50+
}
1951
}
2052

2153
ChamiloHelper::displayLegalTermsPage($return);

src/CoreBundle/Controller/CourseController.php

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -90,14 +90,16 @@ public function checkTermsAndConditionJson(
9090
): Response {
9191
$user = $this->userHelper->getCurrent();
9292
$course = $this->getCourse();
93+
$sid = $this->getSessionId();
94+
9395
$responseData = [
9496
'redirect' => false,
9597
'url' => '#',
9698
];
9799

98100
if ($user->hasRole('ROLE_STUDENT')
99-
&& 'true' === $settingsManager->getSetting('registration.allow_terms_conditions')
100-
&& 'course' === $settingsManager->getSetting('platform.load_term_conditions_section')
101+
&& 'true' === $settingsManager->getSetting('registration.allow_terms_conditions', true)
102+
&& 'course' === $settingsManager->getSetting('platform.load_term_conditions_section', true)
101103
) {
102104
$termAndConditionStatus = false;
103105
$extraValue = $extraFieldValuesRepository->findLegalAcceptByItemId($user->getId());
@@ -115,7 +117,7 @@ public function checkTermsAndConditionJson(
115117

116118
$redirect = true;
117119

118-
if ('true' === $settingsManager->getSetting('course.allow_public_course_with_no_terms_conditions')
120+
if ('true' === $settingsManager->getSetting('course.allow_public_course_with_no_terms_conditions', true)
119121
&& Course::OPEN_WORLD === $course->getVisibility()
120122
) {
121123
$redirect = false;
@@ -124,9 +126,13 @@ public function checkTermsAndConditionJson(
124126
if ($redirect && !$this->isGranted('ROLE_ADMIN')) {
125127
$request->getSession()->remove('cid');
126128
$request->getSession()->remove('course');
129+
130+
// Build return URL
131+
$returnUrl = '/course/' . $course->getId() . '/home?sid='.$sid;
132+
127133
$responseData = [
128134
'redirect' => true,
129-
'url' => '/main/auth/tc.php',
135+
'url' => '/main/auth/tc.php?return=' . urlencode($returnUrl),
130136
];
131137
}
132138
} else {

src/CoreBundle/Controller/SecurityController.php

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -102,9 +102,10 @@ public function loginJson(
102102

103103
$extraFieldValuesRepository = $this->entityManager->getRepository(ExtraFieldValues::class);
104104
$legalTermsRepo = $this->entityManager->getRepository(Legal::class);
105-
if ($user->hasRole('ROLE_STUDENT')
106-
&& 'true' === $this->settingsManager->getSetting('allow_terms_conditions')
107-
&& 'login' === $this->settingsManager->getSetting('load_term_conditions_section')
105+
if (
106+
$user->hasRole('ROLE_STUDENT')
107+
&& 'true' === $this->settingsManager->getSetting('allow_terms_conditions', true)
108+
&& 'login' === $this->settingsManager->getSetting('load_term_conditions_section', true)
108109
) {
109110
$termAndConditionStatus = false;
110111
$extraValue = $extraFieldValuesRepository->findLegalAcceptByItemId($user->getId());
@@ -118,18 +119,15 @@ public function loginJson(
118119
}
119120

120121
if (false === $termAndConditionStatus) {
121-
/*$tempTermAndCondition = ['user_id' => $user->getId()];
122+
$tempTermAndCondition = ['user_id' => $user->getId()];
122123
$this->tokenStorage->setToken(null);
123124
$request->getSession()->invalidate();
124125
$request->getSession()->start();
125126
$request->getSession()->set('term_and_condition', $tempTermAndCondition);
126-
*/
127-
128-
$request->getSession()->set('term_and_condition', ['user_id' => $user->getId()]);
129127

130128
return $this->json([
131-
'requiresTerms' => true,
132-
'redirect' => '/main/auth/tc.php?return=' . urlencode('/'),
129+
'load_terms' => true,
130+
'redirect' => '/main/auth/tc.php?return=' . urlencode('/home'),
133131
]);
134132
}
135133
$request->getSession()->remove('term_and_condition');

src/CoreBundle/DataFixtures/SettingsCurrentFixtures.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3458,6 +3458,11 @@ public static function getNewConfigurationSettings(): array
34583458
'title' => 'Send the welcome message to e-mail and inbox',
34593459
'comment' => "By default, the welcome message (with credentials) is sent only by e-mail. Enable this option to send it to the user's Chamilo inbox as well.",
34603460
],
3461+
[
3462+
'name' => 'hide_legal_accept_checkbox',
3463+
'title' => 'Hide legal accept checkbox in Terms and Conditions page',
3464+
'comment' => 'If set to true, removes the "I have read and accept" checkbox in the Terms and Conditions page flow.',
3465+
],
34613466
],
34623467
'work' => [
34633468
[
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
<?php
2+
3+
/* For licensing terms, see /license.txt */
4+
5+
declare(strict_types=1);
6+
7+
namespace Chamilo\CoreBundle\Migrations\Schema\V200;
8+
9+
use Chamilo\CoreBundle\Migrations\AbstractMigrationChamilo;
10+
use Doctrine\DBAL\Schema\Schema;
11+
12+
final class Version20250714184000 extends AbstractMigrationChamilo
13+
{
14+
public function getDescription(): string
15+
{
16+
return 'Add setting registration.hide_legal_accept_checkbox';
17+
}
18+
19+
public function up(Schema $schema): void
20+
{
21+
$variable = 'hide_legal_accept_checkbox';
22+
$category = 'registration';
23+
$selectedValue = 'false';
24+
$title = 'Hide legal accept checkbox in Terms and Conditions page';
25+
$comment = 'If set to true, removes the "I have read and accept" checkbox in the Terms and Conditions page flow.';
26+
27+
$sqlCheck = sprintf(
28+
"SELECT COUNT(*) as count
29+
FROM settings
30+
WHERE variable = '%s'
31+
AND subkey IS NULL
32+
AND access_url = 1",
33+
addslashes($variable)
34+
);
35+
36+
$stmt = $this->connection->executeQuery($sqlCheck);
37+
$result = $stmt->fetchAssociative();
38+
39+
if ($result && (int) $result['count'] > 0) {
40+
$this->addSql(sprintf(
41+
"UPDATE settings
42+
SET selected_value = '%s',
43+
title = '%s',
44+
comment = '%s',
45+
category = '%s'
46+
WHERE variable = '%s'
47+
AND subkey IS NULL
48+
AND access_url = 1",
49+
addslashes($selectedValue),
50+
addslashes($title),
51+
addslashes($comment),
52+
addslashes($category),
53+
addslashes($variable)
54+
));
55+
$this->write(sprintf('Updated setting: %s', $variable));
56+
} else {
57+
$this->addSql(sprintf(
58+
"INSERT INTO settings
59+
(variable, subkey, type, category, selected_value, title, comment, access_url_changeable, access_url_locked, access_url)
60+
VALUES
61+
('%s', NULL, NULL, '%s', '%s', '%s', '%s', 1, 0, 1)",
62+
addslashes($variable),
63+
addslashes($category),
64+
addslashes($selectedValue),
65+
addslashes($title),
66+
addslashes($comment)
67+
));
68+
$this->write(sprintf('Inserted setting: %s', $variable));
69+
}
70+
}
71+
72+
public function down(Schema $schema): void
73+
{
74+
$variable = 'hide_legal_accept_checkbox';
75+
76+
$this->addSql(sprintf(
77+
"DELETE FROM settings
78+
WHERE variable = '%s'
79+
AND subkey IS NULL
80+
AND access_url = 1",
81+
addslashes($variable)
82+
));
83+
$this->write(sprintf('Removed setting: %s.', $variable));
84+
}
85+
}

src/CoreBundle/Settings/RegistrationSettingsSchema.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ public function buildSettings(AbstractSettingsBuilder $builder): void
3535
'allow_fields_inscription' => '',
3636
'send_inscription_msg_to_inbox' => 'false',
3737
'redirect_after_login' => '',
38+
'hide_legal_accept_checkbox' => 'false',
3839
])
3940
->setTransformer('required_profile_fields', new ArrayToIdentifierTransformer())
4041
->setTransformer('extendedprofile_registration', new ArrayToIdentifierTransformer())
@@ -101,6 +102,7 @@ public function buildForm(FormBuilderInterface $builder): void
101102
->add('redirect_after_login', TextareaType::class, [
102103
'required' => false,
103104
])
105+
->add('hide_legal_accept_checkbox', YesNoType::class)
104106
;
105107

106108
$this->updateFormFieldsFromSettingsInfo($builder);

0 commit comments

Comments
 (0)