User: Add terms and condition when accessing student to a course - refs BT#20846

pull/4858/head
christian 1 year ago
parent 813ef82b2b
commit e3696a6a92
  1. 3
      assets/css/app.scss
  2. 305
      assets/vue/views/course/CourseHome.vue
  3. 4
      public/main/auth/inscription.php
  4. 99
      src/CoreBundle/Controller/CourseController.php
  5. 16
      src/CoreBundle/Repository/ExtraFieldValuesRepository.php
  6. 35
      src/CoreBundle/Repository/LegalRepository.php

@ -139,6 +139,9 @@
}
// Special
.hide-content {
display: none;
}
// Progress bars
.progress {

@ -1,168 +1,170 @@
<template>
<div v-if="isCourseLoading" class="flex flex-col gap-4">
<div class="flex gap-4 items-center">
<Skeleton class="mr-auto" height="2.5rem" width="12rem" />
<Skeleton v-if="isCurrentTeacher" height="2.5rem" width="8rem" />
<Skeleton v-if="isCurrentTeacher" height="2.5rem" width="3rem" />
</div>
<div id="course-home" class="hide-content">
<div v-if="isCourseLoading" class="flex flex-col gap-4">
<div class="flex gap-4 items-center">
<Skeleton class="mr-auto" height="2.5rem" width="12rem" />
<Skeleton v-if="isCurrentTeacher" height="2.5rem" width="8rem" />
<Skeleton v-if="isCurrentTeacher" height="2.5rem" width="3rem" />
</div>
<Skeleton height="16rem" />
<Skeleton height="16rem" />
<div class="flex items-center gap-6">
<Skeleton height="1.5rem" width="6rem" />
<Skeleton v-if="isCurrentTeacher" class="ml-auto" height="1.5rem" width="6rem" />
<Skeleton v-if="isCurrentTeacher" class="aspect-square" height="1.5rem" width="6rem" />
<Skeleton v-if="isCurrentTeacher" class="aspect-square" height="1.5rem" width="6rem" />
<Skeleton v-if="isCurrentTeacher" class="aspect-square" height="1.5rem" width="6rem" />
</div>
<div class="flex items-center gap-6">
<Skeleton height="1.5rem" width="6rem" />
<Skeleton v-if="isCurrentTeacher" class="ml-auto" height="1.5rem" width="6rem" />
<Skeleton v-if="isCurrentTeacher" class="aspect-square" height="1.5rem" width="6rem" />
<Skeleton v-if="isCurrentTeacher" class="aspect-square" height="1.5rem" width="6rem" />
<Skeleton v-if="isCurrentTeacher" class="aspect-square" height="1.5rem" width="6rem" />
</div>
<hr class="mt-0 mb-4" />
<hr class="mt-0 mb-4" />
<div class="grid gap-y-12 sm:gap-x-5 md:gap-x-16 md:gap-y-12 justify-between grid-cols-course-tools">
<Skeleton v-for="v in 30" :key="v" class="aspect-square" height="auto" width="7.5rem" />
</div>
</div>
<div v-else class="flex flex-col gap-4">
<div class="flex gap-4 items-center">
<h2 class="mr-auto">
{{ course.title }}
<small v-if="session"> ({{ session.name }}) </small>
</h2>
<StudentViewButton v-if="course" @change="onStudentViewChanged" />
<BaseButton
v-if="showUpdateIntroductionButton"
:label="t('Edit introduction')"
type="black"
icon="edit"
@click="updateIntro(intro)"
/>
<BaseButton
v-if="isAllowedToEdit"
icon="cog"
only-icon
popup-identifier="course-tmenu"
type="black"
@click="toggleCourseTMenu"
/>
<BaseMenu id="course-tmenu" ref="courseTMenu" :model="courseItems" />
<div class="grid gap-y-12 sm:gap-x-5 md:gap-x-16 md:gap-y-12 justify-between grid-cols-course-tools">
<Skeleton v-for="v in 30" :key="v" class="aspect-square" height="auto" width="7.5rem" />
</div>
</div>
<div v-else class="flex flex-col gap-4">
<div class="flex gap-4 items-center">
<h2 class="mr-auto">
{{ course.title }}
<small v-if="session"> ({{ session.name }}) </small>
</h2>
<hr class="mt-1 mb-1">
<div v-if="isAllowedToEdit" class="mb-4">
<div v-if="intro" class="flex flex-col gap-4">
<div v-html="intro.introText" />
<StudentViewButton v-if="course" @change="onStudentViewChanged" />
<BaseButton
v-if="createInSession && introTool"
:label="t('Course introduction')"
class="ml-auto"
icon="plus"
type="primary"
@click="addIntro(course, intro)"
v-if="showUpdateIntroductionButton"
:label="t('Edit introduction')"
type="black"
icon="edit"
@click="updateIntro(intro)"
/>
</div>
<EmptyState
v-else-if="introTool"
:detail="t('Add a course introduction to display to your students.')"
:summary="t('You don\'t have any course content yet.')"
icon="courses"
>
<BaseButton
:label="t('Course introduction')"
class="mt-4"
icon="plus"
type="primary"
@click="addIntro(course, intro)"
v-if="isAllowedToEdit"
icon="cog"
only-icon
popup-identifier="course-tmenu"
type="black"
@click="toggleCourseTMenu"
/>
</EmptyState>
</div>
<div v-else-if="intro" v-html="intro.introText" class="mb-4" />
<div v-if="isAllowedToEdit" class="flex items-center gap-6">
<h6 v-t="'Tools'" />
<div
class="ml-auto"
>
<BaseToggleButton
:model-value="false"
:on-label="t('Show all')"
:off-label="t('Show all')"
:disabled="isSorting || isCustomizing"
on-icon="eye-on"
off-icon="eye-on"
size="small"
<BaseMenu id="course-tmenu" ref="courseTMenu" :model="courseItems" />
</div>
<hr class="mt-1 mb-1">
<div v-if="isAllowedToEdit" class="mb-4">
<div v-if="intro" class="flex flex-col gap-4">
<div v-html="intro.introText" />
<BaseButton
v-if="createInSession && introTool"
:label="t('Course introduction')"
class="ml-auto"
icon="plus"
type="primary"
@click="addIntro(course, intro)"
/>
</div>
<EmptyState
v-else-if="introTool"
:detail="t('Add a course introduction to display to your students.')"
:summary="t('You don\'t have any course content yet.')"
icon="courses"
>
<BaseButton
:label="t('Course introduction')"
class="mt-4"
icon="plus"
type="primary"
@click="addIntro(course, intro)"
/>
</EmptyState>
</div>
<div v-else-if="intro" v-html="intro.introText" class="mb-4" />
<div v-if="isAllowedToEdit" class="flex items-center gap-6">
<h6 v-t="'Tools'" />
<div
class="ml-auto"
without-borders
@click="onClickShowAll"
/>
<BaseToggleButton
:model-value="false"
:on-label="t('Hide all')"
:off-label="t('Hide all')"
:disabled="isSorting || isCustomizing"
on-icon="eye-off"
off-icon="eye-off"
size="small"
without-borders
@click="onClickHideAll"
/>
<BaseToggleButton
v-model="isSorting"
:disabled="isCustomizing"
:on-label="t('Sort')"
on-icon="swap-vertical"
:off-label="t('Sort')"
off-icon="swap-vertical"
size="small"
without-borders
>
<BaseToggleButton
:model-value="false"
:on-label="t('Show all')"
:off-label="t('Show all')"
:disabled="isSorting || isCustomizing"
on-icon="eye-on"
off-icon="eye-on"
size="small"
class="ml-auto"
without-borders
@click="onClickShowAll"
/>
<BaseToggleButton
:model-value="false"
:on-label="t('Hide all')"
:off-label="t('Hide all')"
:disabled="isSorting || isCustomizing"
on-icon="eye-off"
off-icon="eye-off"
size="small"
without-borders
@click="onClickHideAll"
/>
<BaseToggleButton
v-model="isSorting"
:disabled="isCustomizing"
:on-label="t('Sort')"
on-icon="swap-vertical"
:off-label="t('Sort')"
off-icon="swap-vertical"
size="small"
without-borders
/>
<BaseToggleButton
v-model="isCustomizing"
:disabled="isSorting"
:on-label="t('Customize')"
on-icon="customize"
:off-label="t('Customize')"
off-icon="customize"
size="small"
without-borders
/>
</div>
</div>
<hr class="mt-0 mb-4" />
<div id="course-tools" class="grid gap-y-12 sm:gap-x-5 md:gap-x-16 md:gap-y-12 grid-cols-course-tools">
<CourseTool
v-for="(tool, index) in tools"
:key="'tool-' + index.toString()"
:change-visibility="changeVisibility"
:course="course"
:to="tool.to"
:url="tool.url"
:go-to-setting-course-tool="goToSettingCourseTool"
:tool="tool"
:data-tool="tool.ctool.name"
:data-index="index"
/>
<BaseToggleButton
v-model="isCustomizing"
:disabled="isSorting"
:on-label="t('Customize')"
on-icon="customize"
:off-label="t('Customize')"
off-icon="customize"
size="small"
without-borders
<ShortCutList
v-for="(shortcut, index) in shortcuts"
:key="'shortcut-' + index.toString()"
:change-visibility="changeVisibility"
:go-to-short-cut="goToShortCut"
:shortcut="shortcut"
/>
</div>
</div>
<hr class="mt-0 mb-4" />
<div id="course-tools" class="grid gap-y-12 sm:gap-x-5 md:gap-x-16 md:gap-y-12 grid-cols-course-tools">
<CourseTool
v-for="(tool, index) in tools"
:key="'tool-' + index.toString()"
:change-visibility="changeVisibility"
:course="course"
:to="tool.to"
:url="tool.url"
:go-to-setting-course-tool="goToSettingCourseTool"
:tool="tool"
:data-tool="tool.ctool.name"
:data-index="index"
/>
<ShortCutList
v-for="(shortcut, index) in shortcuts"
:key="'shortcut-' + index.toString()"
:change-visibility="changeVisibility"
:go-to-short-cut="goToShortCut"
:shortcut="shortcut"
/>
</div>
</div>
</template>
<script setup>
import {computed, onMounted, provide, ref, watch} from "vue"
import {computed, onMounted, provide, ref, watch, onBeforeMount} from "vue"
import { useStore } from "vuex";
import { useRoute, useRouter } from "vue-router";
import { useI18n } from "vue-i18n";
@ -402,6 +404,21 @@ async function updateDisplayOrder(htmlItem, newIndex) {
const isAllowedToEdit = ref(false)
onBeforeMount(async () => {
try {
const response = await axios.get(ENTRYPOINT + `../course/${courseId}/checkLegal.json`);
if (response.data.redirect) {
window.location.href = response.data.url;
} else {
document.getElementById('course-home').classList.remove('hide-content');
}
} catch (error) {
console.error("Error checking terms and conditions:", error);
document.getElementById('course-home').classList.remove('hide-content');
}
});
onMounted(async () => {
isAllowedToEdit.value = await checkIsAllowedToEdit()
setTimeout(() => {

@ -589,7 +589,7 @@ if ('true' === api_get_setting('allow_terms_conditions')) {
$termExtraFields = new ExtraFieldValue('terms_and_condition');
$values = $termExtraFields->getAllValuesByItem($termPreview['id']);
foreach ($values as $value) {
echo '<h3>'.$value['display_text'].'</h3><br />'.$value['value'].'<br />';
echo '<h3>'.$value['display_text'].'</h3><br />'.$value['field_value'].'<br />';
}
} else {
echo get_lang('Coming soon...');
@ -720,7 +720,7 @@ if ('true' === api_get_setting('allow_terms_conditions')) {
$values = $termExtraFields->getAllValuesByItem($termPreview['id']);
foreach ($values as $value) {
//if ($value['variable'] === 'category') {
$form->addLabel($value['display_text'], $value['value']);
$form->addLabel($value['display_text'], $value['field_value']);
//}
}
}

@ -13,11 +13,13 @@ use Chamilo\CoreBundle\Entity\Tag;
use Chamilo\CoreBundle\Entity\Tool;
use Chamilo\CoreBundle\Entity\User;
use Chamilo\CoreBundle\Framework\Container;
use Chamilo\CoreBundle\Repository\ExtraFieldValuesRepository;
use Chamilo\CoreBundle\Repository\LanguageRepository;
use Chamilo\CoreBundle\Repository\LegalRepository;
use Chamilo\CoreBundle\Repository\Node\IllustrationRepository;
use Chamilo\CoreBundle\Repository\TagRepository;
use Chamilo\CoreBundle\Security\Authorization\Voter\CourseVoter;
use Chamilo\CoreBundle\Settings\SettingsManager;
use Chamilo\CoreBundle\Tool\AbstractTool;
use Chamilo\CoreBundle\Tool\ToolChain;
use Chamilo\CourseBundle\Controller\ToolBaseController;
@ -62,7 +64,9 @@ class CourseController extends ToolBaseController
public function checkTermsAndConditionJson(
Request $request,
LegalRepository $legalTermsRepo,
LanguageRepository $languageRepository
LanguageRepository $languageRepository,
ExtraFieldValuesRepository $extraFieldValuesRepository,
SettingsManager $settingsManager
): Response {
/** @var User $user */
$user = $this->getUser();
@ -72,64 +76,46 @@ class CourseController extends ToolBaseController
'url' => '#',
];
$termAndConditionStatus = $legalTermsRepo->checkTermCondition($user);
if (false === $termAndConditionStatus) {
$request->getSession()->set('term_and_condition', ['user_id' => $user->getId()]);
} else {
$request->getSession()->remove('term_and_condition');
}
$termsAndCondition = $request->getSession()->get('term_and_condition');
if (null !== $termsAndCondition) {
// user id
$userId = $termsAndCondition['user_id'];
// Update the terms & conditions
$legalType = null;
// Verify type of terms and conditions
if (null !== $request->get('legal_info')) {
$infoLegal = explode(':', $request->get('legal_info'));
$legalId = (int) $infoLegal[0];
$languageId = (int) $infoLegal[1];
$legal = $legalTermsRepo->find($legalId);
$language = $languageRepository->find($languageId);
$legalType = $legalTermsRepo->getTypeOfTermsAndConditions($legal, $language);
if ($user && $user->hasRole('ROLE_STUDENT') &&
'true' === $settingsManager->getSetting('allow_terms_conditions') &&
'course' === $settingsManager->getSetting('load_term_conditions_section')
) {
$termAndConditionStatus = false;
$extraValue = $extraFieldValuesRepository->findLegalAcceptByItemId($user->getId());
if (!empty($extraValue['value'])) {
$result = $extraValue['value'];
$userConditions = explode(':', $result);
$version = $userConditions[0];
$langId = (int) $userConditions[1];
$realVersion = $legalTermsRepo->getLastVersion($langId);
$termAndConditionStatus = ($version >= $realVersion);
}
$legalOption = (empty($legalType));
// is necessary verify check
if (1 === $legalType) {
$legalOption = (null !== $request->get('legal_accept') && 1 === (int) $request->get('legal_accept'));
if (false === $termAndConditionStatus) {
$request->getSession()->set('term_and_condition', ['user_id' => $user->getId()]);
} else {
$request->getSession()->remove('term_and_condition');
}
if (null !== $request->get('legal_accept_type') && true === $legalOption) {
$condArray = explode(':', $request->get('legal_accept_type'));
if (!empty($condArray[0]) && !empty($condArray[1])) {
$time = time();
$conditionToSave = (int) ($condArray[0]).':'.(int) ($condArray[1]).':'.$time;
UserManager::update_extra_field_value(
$userId,
'legal_accept',
$conditionToSave
);
$termsAndCondition = $request->getSession()->get('term_and_condition');
if (null !== $termsAndCondition) {
$redirect = true;
$allow = 'true' === Container::getSettingsManager()
->getSetting('course.allow_public_course_with_no_terms_conditions');
if (true === $allow &&
null !== $course->getVisibility() &&
COURSE_VISIBILITY_OPEN_WORLD === $course->getVisibility()
) {
$redirect = false;
}
if ($redirect && !$this->isGranted('ROLE_ADMIN')) {
$url = '/main/auth/inscription.php';
$responseData = [
'redirect' => $redirect,
'url' => $url,
];
}
}
$redirect = true;
$allow = 'true' === Container::getSettingsManager()
->getSetting('course.allow_public_course_with_no_terms_conditions');
if (true === $allow &&
null !== $course->getVisibility() &&
COURSE_VISIBILITY_OPEN_WORLD === $course->getVisibility()
) {
$redirect = false;
}
if ($redirect && !$this->isGranted('ROLE_ADMIN')) {
$url = '/main/auth/inscription.php';
$responseData = [
'redirect' => $redirect,
'url' => $url,
];
}
}
@ -653,10 +639,6 @@ class CourseController extends ToolBaseController
}
}
error_log('$ctool -> '.$ctool->getIid());
error_log('$createInSession -> '.$createInSession);
if ($ctool) {
$ctoolintroRepo = $em->getRepository(CToolIntro::class);
/** @var CToolIntro $ctoolintro */
@ -678,7 +660,6 @@ class CourseController extends ToolBaseController
#[Route('/{id}/addToolIntro', name: 'chamilo_core_course_addtoolintro')]
public function addToolIntro(Request $request, Course $course, EntityManagerInterface $em): Response
{
error_log('In addToolIntro');
$data = $request->getContent();
$data = json_decode($data);

@ -111,4 +111,20 @@ class ExtraFieldValuesRepository extends ServiceEntityRepository
return $extraFieldValues;
}
public function findLegalAcceptByItemId($itemId)
{
$qb = $this->createQueryBuilder('s')
->innerJoin('s.field', 'sf')
->where('s.itemId = :itemId')
->andWhere('sf.variable = :variable')
->andWhere('sf.itemType = :itemType')
->orderBy('s.id', 'ASC')
->setMaxResults(1)
->setParameter('itemId', $itemId)
->setParameter('variable', 'legal_accept')
->setParameter('itemType', 1);
return $qb->getQuery()->getOneOrNullResult();
}
}

@ -81,41 +81,6 @@ class LegalRepository extends ServiceEntityRepository
return $qb->getQuery()->getResult();
}
/**
* Checks whether we already approved the last version term and condition.
*
* @return bool true if we pass false otherwise
*/
public function checkTermCondition(User $user)
{
if ('true' === api_get_setting('allow_terms_conditions')) {
// Check if exists terms and conditions
if (0 === $this->countTerms()) {
return true;
}
$extraFieldValue = new ExtraFieldValue('user');
$data = $extraFieldValue->get_values_by_handler_and_field_variable(
$user->getId(),
'legal_accept'
);
if (!empty($data) && isset($data['value']) && !empty($data['value'])) {
$result = $data['value'];
$userConditions = explode(':', $result);
$version = $userConditions[0];
$langId = (int) $userConditions[1];
$realVersion = $this->getLastVersion($langId);
return $version >= $realVersion;
}
return false;
}
return false;
}
/**
* Gets the number of terms and conditions available.
*

Loading…
Cancel
Save