Course: Start restoring Student View button - refs #4678

pull/4726/head
Angel Fernando Quiroz Campos 2 years ago
parent 207f095c67
commit d728437375
  1. 37
      assets/vue/components/StudentViewButton.vue
  2. 45
      assets/vue/components/basecomponents/BaseToggleButton.vue
  3. 8
      assets/vue/components/basecomponents/ChamiloIcons.js
  4. 3
      assets/vue/store/platformConfig.js
  5. 3
      assets/vue/views/course/CourseHome.vue
  6. 24
      src/CoreBundle/Controller/IndexController.php
  7. 8
      src/CoreBundle/Controller/PlatformConfigurationController.php
  8. 17
      src/CoreBundle/EventListener/LegacyListener.php
  9. 12
      src/CoreBundle/Repository/ResourceRepository.php
  10. 14
      tests/CoreBundle/Controller/IndexControllerTest.php

@ -0,0 +1,37 @@
<template>
<BaseToggleButton
v-if="isCurrentTeacher && 'true' === platformConfigurationStore.getSetting('course.student_view_enabled')"
v-model="isStudentView"
:off-label="t('Switch to student view')"
:on-label="t('Switch to teacher view')"
off-icon="eye"
on-icon="eye"
/>
</template>
<script setup>
import BaseToggleButton from "./basecomponents/BaseToggleButton.vue";
import { computed, ref, watch } from "vue";
import { useI18n } from "vue-i18n";
import { useRoute } from "vue-router";
import { useStore } from "vuex";
import { usePlatformConfig } from "../store/platformConfig";
const route = useRoute();
const store = useStore();
const { t } = useI18n();
const platformConfigurationStore = usePlatformConfig();
const isStudentView = ref('studentview' === platformConfigurationStore.studentView);
watch(isStudentView, (newValue) => {
const params = new URLSearchParams(window.location.search);
params.delete('isStudentView');
params.append('isStudentView', newValue ? 'true' : 'false');
window.location.href = route.path + '?' + params.toString();
});
const isCurrentTeacher = computed(() => store.getters["security/isCurrentTeacher"]);
</script>

@ -0,0 +1,45 @@
<template>
<ToggleButton
v-model="primeModelValue"
:off-icon="chamiloIconToClass[offIcon]"
:off-label="offLabel"
:on-icon="chamiloIconToClass[onIcon]"
:on-label="onLabel"
@change="$emit('update:modelValue', primeModelValue)"
/>
</template>
<script setup>
import ToggleButton from "primevue/togglebutton";
import { ref } from "vue";
import { chamiloIconToClass, validator } from "./ChamiloIcons";
const props = defineProps({
modelValue: {
type: Boolean,
required: true,
},
onLabel: {
type: String,
required: true,
},
onIcon: {
type: String,
required: true,
validator,
},
offLabel: {
type: String,
required: true,
},
offIcon: {
type: String,
required: true,
validator,
},
});
defineEmits(["update:modelValue"]);
const primeModelValue = ref(props.modelValue);
</script>

@ -53,3 +53,11 @@ export const chamiloIconToClass = {
"cog": "mdi mdi-cog",
"plus": "mdi mdi-plus",
};
export const validator = (value) => {
if (typeof (value) !== "string") {
return false;
}
return Object.keys(chamiloIconToClass).includes(value);
};

@ -5,6 +5,7 @@ import { ref } from "vue";
export const usePlatformConfig = defineStore("platformConfig", () => {
const isLoading = ref(false);
const settings = ref(null);
const studentView = ref(null);
function getSetting(variable) {
if (settings.value && settings.value[variable]) {
@ -20,6 +21,7 @@ export const usePlatformConfig = defineStore("platformConfig", () => {
const { data } = await axios.get("/platform-config/list");
settings.value = data.settings;
studentView.value = data.studentview;
isLoading.value = false;
}
@ -31,6 +33,7 @@ export const usePlatformConfig = defineStore("platformConfig", () => {
return {
isLoading,
settings,
studentView,
initialize,
getSetting,
};

@ -29,7 +29,7 @@
<small v-if="session"> ({{ session.name }}) </small>
</h2>
<BaseButton v-if="course && isCurrentTeacher" :label="t('See as student')" icon="eye" type="black" />
<StudentViewButton v-if="course" />
<BaseButton
v-if="showUpdateIntroductionButton"
@ -180,6 +180,7 @@ import EmptyState from "../../components/EmptyState";
import Skeleton from "primevue/skeleton";
import BaseButton from "../../components/basecomponents/BaseButton.vue";
import BaseMenu from "../../components/basecomponents/BaseMenu.vue";
import StudentViewButton from "../../components/StudentViewButton.vue";
const route = useRoute();
const store = useStore();

@ -36,30 +36,6 @@ class IndexController extends BaseController
return $this->render('@ChamiloCore/Index/vue.html.twig');
}
/**
* Toggle the student view action.
*
* @Route("/toggle_student_view", methods={"GET"})
*
* @Security("is_granted('ROLE_TEACHER')")
*/
public function toggleStudentViewAction(Request $request): Response
{
if (!api_is_allowed_to_edit(false, false, false, false)) {
throw $this->createAccessDeniedException();
}
$studentView = $request->getSession()->get('studentview');
if (empty($studentView) || 'studentview' === $studentView) {
$content = 'teacherview';
} else {
$content = 'studentview';
}
$request->getSession()->set('studentview', $content);
return new Response($content);
}
/**
* Use only in PHPUnit tests.
*/

@ -7,6 +7,8 @@ declare(strict_types=1);
namespace Chamilo\CoreBundle\Controller;
use Chamilo\CoreBundle\Settings\SettingsManager;
use Chamilo\CoreBundle\Traits\ControllerTrait;
use Chamilo\CoreBundle\Traits\CourseControllerTrait;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;
@ -16,11 +18,16 @@ use TicketManager;
#[Route('/platform-config')]
class PlatformConfigurationController extends AbstractController
{
use ControllerTrait;
#[Route('/list', name: 'platform_config_list', methods: ['GET'])]
public function list(SettingsManager $settingsManager): Response
{
$requestSession = $this->getRequest()->getSession();
$configuration = [
'settings' => [],
'studentview' => $requestSession->get('studentview'),
];
$variables = [];
@ -57,6 +64,7 @@ class PlatformConfigurationController extends AbstractController
'gradebook.gradebook_dependency',
'course.course_validation',
'course.student_view_enabled',
'session.limit_session_admin_role',
'session.allow_session_admin_read_careers',

@ -10,6 +10,7 @@ use Chamilo\CoreBundle\Entity\User;
use Chamilo\CoreBundle\Framework\Container;
use Chamilo\CoreBundle\Repository\Node\AccessUrlRepository;
use Chamilo\CoreBundle\Repository\Node\UserRepository;
use Chamilo\CoreBundle\Settings\SettingsManager;
use Exception;
use Symfony\Component\DependencyInjection\ContainerAwareTrait;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
@ -37,7 +38,7 @@ class LegacyListener
private RouterInterface $router;
private ParameterBagInterface $parameterBag;
public function __construct(Environment $twig, TokenStorageInterface $tokenStorage, UserRepository $userRepository, AccessUrlRepository $accessUrlRepository, RouterInterface $router, ParameterBagInterface $parameterBag)
public function __construct(Environment $twig, TokenStorageInterface $tokenStorage, UserRepository $userRepository, AccessUrlRepository $accessUrlRepository, RouterInterface $router, ParameterBagInterface $parameterBag, private readonly SettingsManager $settingsManager)
{
$this->twig = $twig;
$this->tokenStorage = $tokenStorage;
@ -103,6 +104,20 @@ class LegacyListener
$session->set('is_platformAdmin', $isAdmin);
$session->set('is_allowedCreateCourse', $allowedCreateCourse);
if ('true' === $this->settingsManager->getSetting('course.student_view_enabled')) {
if ($request->query->has('isStudentView')) {
$isStudentView = $request->query->get('isStudentView');
if ('true' === $isStudentView) {
$session->set('studentview', 'studentview');
} elseif ('false' === $isStudentView) {
$session->set('studentview', 'teacherview');
}
} elseif (!$session->has('studentview')) {
$session->set('studentview', 'teacherview');
}
}
// Theme icon is loaded in the TwigListener src/ThemeBundle/EventListener/TwigListener.php
//$theme = api_get_visual_theme();
/*$languages = api_get_languages();

@ -266,12 +266,14 @@ abstract class ResourceRepository extends ServiceEntityRepository
]);
}
public function addVisibilityQueryBuilder(QueryBuilder $qb = null): QueryBuilder
public function addVisibilityQueryBuilder(QueryBuilder $qb = null, bool $checkStudentView = false): QueryBuilder
{
$qb = $this->getOrCreateQueryBuilder($qb);
$sessionStudentView = $this->getRequest()->getSession()->get('studentview');
$checker = $this->getAuthorizationChecker();
$isAdmin =
$isAdminOrTeacher =
$checker->isGranted('ROLE_ADMIN') ||
$checker->isGranted('ROLE_CURRENT_COURSE_TEACHER');
@ -281,7 +283,9 @@ abstract class ResourceRepository extends ServiceEntityRepository
->setParameter('visibilityDeleted', ResourceLink::VISIBILITY_DELETED, Types::INTEGER)
;
if (!$isAdmin) {
if (!$isAdminOrTeacher
|| ($checkStudentView && 'studentview' === $sessionStudentView)
) {
$qb
->andWhere('links.visibility = :visibility')
->setParameter('visibility', ResourceLink::VISIBILITY_PUBLISHED, Types::INTEGER)
@ -382,7 +386,7 @@ abstract class ResourceRepository extends ServiceEntityRepository
public function getResourcesByCourse(Course $course, Session $session = null, CGroup $group = null, ResourceNode $parentNode = null): QueryBuilder
{
$qb = $this->getResources($parentNode);
$this->addVisibilityQueryBuilder($qb);
$this->addVisibilityQueryBuilder($qb, true);
$this->addCourseSessionGroupQueryBuilder($course, $session, $group, $qb);
return $qb;

@ -73,20 +73,6 @@ class IndexControllerTest extends WebTestCase
$this->assertStringContainsString('lang="fr_FR"', $client->getResponse()->getContent());
}
public function testToggleStudentViewAction(): void
{
$client = static::createClient();
// retrieve the admin
$admin = $this->getUser('admin');
// simulate $testUser being logged in
$client->loginUser($admin);
$client->request('GET', '/toggle_student_view');
$this->assertResponseIsSuccessful();
}
public function testLogout(): void
{
$client = static::createClient();

Loading…
Cancel
Save