Internal: Fix StudentViewButton component for session coaches - refs BT#22166

pull/5936/head
Angel Fernando Quiroz Campos 4 days ago
parent dcebac36a0
commit af9cf8f7cd
No known key found for this signature in database
GPG Key ID: B284841AE3E562CD
  1. 9
      assets/vue/components/StudentViewButton.vue
  2. 44
      assets/vue/composables/userPermissions.js
  3. 10
      assets/vue/router/index.js
  4. 41
      assets/vue/store/cidReq.js
  5. 13
      assets/vue/store/securityStore.js
  6. 4
      src/CoreBundle/Controller/IndexController.php
  7. 19
      src/CoreBundle/Entity/Session.php
  8. 7
      src/CoreBundle/Entity/SessionRelCourseRelUser.php
  9. 2
      src/CoreBundle/Entity/SessionRelUser.php

@ -14,10 +14,10 @@ import BaseToggleButton from "./basecomponents/BaseToggleButton.vue"
import { computed } from "vue" import { computed } from "vue"
import { useI18n } from "vue-i18n" import { useI18n } from "vue-i18n"
import { usePlatformConfig } from "../store/platformConfig" import { usePlatformConfig } from "../store/platformConfig"
import { storeToRefs } from "pinia"
import { useCidReqStore } from "../store/cidReq" import { useCidReqStore } from "../store/cidReq"
import { useSecurityStore } from "../store/securityStore" import { useSecurityStore } from "../store/securityStore"
import permissionService from "../services/permissionService" import permissionService from "../services/permissionService"
import { useUserSessionSubscription } from "../composables/userPermissions"
const emit = defineEmits(["change"]) const emit = defineEmits(["change"])
@ -25,6 +25,7 @@ const { t } = useI18n()
const platformConfigStore = usePlatformConfig() const platformConfigStore = usePlatformConfig()
const cidReqStore = useCidReqStore() const cidReqStore = useCidReqStore()
const securityStore = useSecurityStore() const securityStore = useSecurityStore()
const { isCoach } = useUserSessionSubscription()
const isStudentView = computed({ const isStudentView = computed({
async set() { async set() {
@ -39,13 +40,11 @@ const isStudentView = computed({
}, },
}) })
const { course, userIsCoach } = storeToRefs(cidReqStore)
const showButton = computed(() => { const showButton = computed(() => {
return ( return (
securityStore.isAuthenticated && securityStore.isAuthenticated &&
course.value && cidReqStore.course &&
(securityStore.isCourseAdmin || securityStore.isAdmin || userIsCoach.value(securityStore.user.id, 0, false)) && (securityStore.isCourseAdmin || securityStore.isAdmin || isCoach.value) &&
"true" === platformConfigStore.getSetting("course.student_view_enabled") "true" === platformConfigStore.getSetting("course.student_view_enabled")
) )
}) })

@ -1,6 +1,8 @@
import { storeToRefs } from "pinia" import { storeToRefs } from "pinia"
import { useCidReqStore } from "../store/cidReq" import { useCidReqStore } from "../store/cidReq"
import api from "../config/api" import api from "../config/api"
import { computed, ref, unref } from "vue"
import { useSecurityStore } from "../store/securityStore"
/** /**
* @param {boolean} tutor * @param {boolean} tutor
@ -37,3 +39,45 @@ export async function checkIsAllowedToEdit(
return false return false
} }
export function useUserSessionSubscription(session = null, course = null) {
const isGeneralCoach = ref(false)
const isCurrentCourseCoach = ref(false)
const isCourseCoach = ref(false)
const isCoach = computed(() => isGeneralCoach.value || isCurrentCourseCoach.value || isCourseCoach.value)
const cidReqStore = useCidReqStore()
const securityStore = useSecurityStore()
session = session || unref(cidReqStore.session)
course = course || unref(cidReqStore.course)
if (session) {
isGeneralCoach.value = session.generalCoachesSubscriptions.some(
(sessionRelUser) => sessionRelUser.user === securityStore.user["@id"],
)
for (const sessionRelCourseRelUser of session.courseCoachesSubscriptions) {
if (securityStore.user["@id"] === sessionRelCourseRelUser.user) {
isCourseCoach.value = true
if (course) {
if (course["@id"] === sessionRelCourseRelUser.course) {
isCurrentCourseCoach.value = true
break
}
} else {
break
}
}
}
}
return {
isGeneralCoach,
isCurrentCourseCoach,
isCourseCoach,
isCoach,
}
}

@ -21,7 +21,6 @@ import assignments from "./assignments"
import links from "./links" import links from "./links"
import glossary from "./glossary" import glossary from "./glossary"
import { useSecurityStore } from "../store/securityStore" import { useSecurityStore } from "../store/securityStore"
import securityService from "../services/securityService"
import MyCourseList from "../views/user/courses/List.vue" import MyCourseList from "../views/user/courses/List.vue"
import MySessionList from "../views/user/sessions/SessionsCurrent.vue" import MySessionList from "../views/user/sessions/SessionsCurrent.vue"
import MySessionListPast from "../views/user/sessions/SessionsPast.vue" import MySessionListPast from "../views/user/sessions/SessionsPast.vue"
@ -44,6 +43,7 @@ import courseService from "../services/courseService"
import catalogueCourses from "./cataloguecourses" import catalogueCourses from "./cataloguecourses"
import catalogueSessions from "./cataloguesessions" import catalogueSessions from "./cataloguesessions"
import { customVueTemplateEnabled } from "../config/env" import { customVueTemplateEnabled } from "../config/env"
import { useUserSessionSubscription } from "../composables/userPermissions"
const router = createRouter({ const router = createRouter({
history: createWebHistory(), history: createWebHistory(),
@ -208,10 +208,12 @@ router.beforeResolve(async (to) => {
await cidReqStore.setCourseAndSessionById(cid, sid) await cidReqStore.setCourseAndSessionById(cid, sid)
if (cidReqStore.session) { if (cidReqStore.session) {
const isGeneralCoach = cidReqStore.session.generalCoaches.includes(securityStore.user["@id"]) const { isGeneralCoach, isCourseCoach } = useUserSessionSubscription()
const isCourseCoach = cidReqStore.session.courseCoaches.includes(securityStore.user["@id"])
if (isGeneralCoach || isCourseCoach) { securityStore.removeRole("ROLE_CURRENT_COURSE_SESSION_TEACHER")
securityStore.removeRole("ROLE_CURRENT_COURSE_SESSION_STUDENT")
if (isGeneralCoach.value || isCourseCoach.value) {
securityStore.user.roles.push("ROLE_CURRENT_COURSE_SESSION_TEACHER") securityStore.user.roles.push("ROLE_CURRENT_COURSE_SESSION_TEACHER")
} else { } else {
securityStore.user.roles.push("ROLE_CURRENT_COURSE_SESSION_STUDENT") securityStore.user.roles.push("ROLE_CURRENT_COURSE_SESSION_STUDENT")

@ -1,7 +1,6 @@
import { defineStore } from "pinia" import { defineStore } from "pinia"
import { usePlatformConfig } from "./platformConfig"
import courseService from "../services/courseService" import courseService from "../services/courseService"
import { computed, ref } from "vue" import { ref } from "vue"
import sessionService from "../services/sessionService" import sessionService from "../services/sessionService"
import { useCourseSettings } from "./courseSettingStore" import { useCourseSettings } from "./courseSettingStore"
@ -13,42 +12,6 @@ export const useCidReqStore = defineStore("cidReq", () => {
const courseSettingsStore = useCourseSettings() const courseSettingsStore = useCourseSettings()
const userIsCoach = computed(() => {
const platformConfigStore = usePlatformConfig()
return (userId, cId = 0, checkStudentView = true) => {
if (checkStudentView && platformConfigStore.isStudentViewActive) {
return false
}
if (!session.value || !userId) {
return false
}
const sessionIsCoach = []
if (cId) {
const courseCoachSubscription = session.value?.sessionRelCourseRelUsers?.find(
(srcru) => srcru.course.id === cId && srcru.user.id === userId && 2 === srcru.status,
)
if (courseCoachSubscription) {
sessionIsCoach.push(courseCoachSubscription)
}
}
const generalCoachSubscription = session.value?.users?.find(
(sru) => sru.user.id === userId && 3 === sru.relationType,
)
if (generalCoachSubscription) {
sessionIsCoach.push(generalCoachSubscription)
}
return sessionIsCoach.length > 0
}
})
const resetCid = () => { const resetCid = () => {
course.value = null course.value = null
session.value = null session.value = null
@ -113,8 +76,6 @@ export const useCidReqStore = defineStore("cidReq", () => {
session, session,
group, group,
userIsCoach,
resetCid, resetCid,
setCourseAndSessionById, setCourseAndSessionById,

@ -16,6 +16,17 @@ export const useSecurityStore = defineStore("security", () => {
return false return false
}) })
/**
* @param {string} role
*/
const removeRole = (role) => {
const index = user.value.roles.indexOf(role)
if (index > -1) {
user.value.roles.splice(index, 1)
}
}
const isStudent = computed(() => hasRole.value("ROLE_STUDENT")) const isStudent = computed(() => hasRole.value("ROLE_STUDENT"))
const isStudentBoss = computed(() => hasRole.value("ROLE_STUDENT_BOSS")) const isStudentBoss = computed(() => hasRole.value("ROLE_STUDENT_BOSS"))
@ -36,7 +47,6 @@ export const useSecurityStore = defineStore("security", () => {
const isAdmin = computed(() => hasRole.value("ROLE_SUPER_ADMIN") || hasRole.value("ROLE_ADMIN")) const isAdmin = computed(() => hasRole.value("ROLE_SUPER_ADMIN") || hasRole.value("ROLE_ADMIN"))
async function checkSession() { async function checkSession() {
isLoading.value = true isLoading.value = true
try { try {
@ -59,6 +69,7 @@ export const useSecurityStore = defineStore("security", () => {
isLoading, isLoading,
isAuthenticated, isAuthenticated,
hasRole, hasRole,
removeRole,
isStudent, isStudent,
isStudentBoss, isStudentBoss,
isHRM, isHRM,

@ -71,10 +71,6 @@ class IndexController extends BaseController
#[Security("is_granted('ROLE_TEACHER')")] #[Security("is_granted('ROLE_TEACHER')")]
public function toggleStudentView(Request $request, SettingsManager $settingsManager): Response public function toggleStudentView(Request $request, SettingsManager $settingsManager): Response
{ {
if (!api_is_allowed_to_edit(false, false, false, false)) {
throw $this->createAccessDeniedException();
}
if ('true' !== $settingsManager->getSetting('course.student_view_enabled')) { if ('true' !== $settingsManager->getSetting('course.student_view_enabled')) {
throw $this->createAccessDeniedException(); throw $this->createAccessDeniedException();
} }

@ -768,15 +768,20 @@ class Session implements ResourceWithAccessUrlInterface, Stringable
return $this; return $this;
} }
#[Groups(['session:basic'])] /**
public function getGeneralCoaches(): ReadableCollection * @return Collection<int, SessionRelUser>
*/
public function getGeneralCoaches(): Collection
{ {
return $this->getGeneralCoachesSubscriptions() return $this->getGeneralCoachesSubscriptions()
->map(fn (SessionRelUser $subscription) => $subscription->getUser()) ->map(fn (SessionRelUser $subscription) => $subscription->getUser())
; ;
} }
#[Groups(['user_subscriptions:sessions'])] /**
* @return Collection<int, SessionRelUser>
*/
#[Groups(['session:basic', 'user_subscriptions:sessions'])]
public function getGeneralCoachesSubscriptions(): Collection public function getGeneralCoachesSubscriptions(): Collection
{ {
$criteria = Criteria::create()->where(Criteria::expr()->eq('relationType', self::GENERAL_COACH)); $criteria = Criteria::create()->where(Criteria::expr()->eq('relationType', self::GENERAL_COACH));
@ -1305,8 +1310,10 @@ class Session implements ResourceWithAccessUrlInterface, Stringable
return !empty($end) && $now <= $end; return !empty($end) && $now <= $end;
} }
#[Groups(['session:basic'])] /**
public function getCourseCoaches() * @return Collection<int, SessionRelCourseRelUser>
*/
public function getCourseCoaches(): Collection
{ {
return $this->getCourseCoachesSubscriptions() return $this->getCourseCoachesSubscriptions()
->map(fn (SessionRelCourseRelUser $subscription) => $subscription->getUser()) ->map(fn (SessionRelCourseRelUser $subscription) => $subscription->getUser())
@ -1316,7 +1323,7 @@ class Session implements ResourceWithAccessUrlInterface, Stringable
/** /**
* @return Collection<int, SessionRelCourseRelUser> * @return Collection<int, SessionRelCourseRelUser>
*/ */
#[Groups(['user_subscriptions:sessions'])] #[Groups(['session:basic', 'user_subscriptions:sessions'])]
public function getCourseCoachesSubscriptions(): Collection public function getCourseCoachesSubscriptions(): Collection
{ {
return $this->getAllUsersFromCourse(self::COURSE_COACH); return $this->getAllUsersFromCourse(self::COURSE_COACH);

@ -10,7 +10,6 @@ use ApiPlatform\Doctrine\Orm\Filter\DateFilter;
use ApiPlatform\Doctrine\Orm\Filter\SearchFilter; use ApiPlatform\Doctrine\Orm\Filter\SearchFilter;
use ApiPlatform\Metadata\ApiFilter; use ApiPlatform\Metadata\ApiFilter;
use ApiPlatform\Metadata\ApiResource; use ApiPlatform\Metadata\ApiResource;
use Chamilo\CoreBundle\Entity\User as UserAlias;
use Chamilo\CoreBundle\Traits\UserTrait; use Chamilo\CoreBundle\Traits\UserTrait;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Annotation\Groups; use Symfony\Component\Serializer\Annotation\Groups;
@ -75,10 +74,11 @@ class SessionRelCourseRelUser
'session:read', 'session:read',
'session_rel_course_rel_user:read', 'session_rel_course_rel_user:read',
'user_subscriptions:sessions', 'user_subscriptions:sessions',
'session:basic',
])] ])]
#[ORM\ManyToOne(targetEntity: UserAlias::class, cascade: ['persist'], inversedBy: 'sessionRelCourseRelUsers')] #[ORM\ManyToOne(targetEntity: User::class, cascade: ['persist'], inversedBy: 'sessionRelCourseRelUsers')]
#[ORM\JoinColumn(name: 'user_id', referencedColumnName: 'id', nullable: false, onDelete: 'CASCADE')] #[ORM\JoinColumn(name: 'user_id', referencedColumnName: 'id', nullable: false, onDelete: 'CASCADE')]
protected UserAlias $user; protected User $user;
#[Groups([ #[Groups([
'session_rel_course_rel_user:read', 'session_rel_course_rel_user:read',
@ -92,6 +92,7 @@ class SessionRelCourseRelUser
'session_rel_course_rel_user:read', 'session_rel_course_rel_user:read',
'session_rel_user:read', 'session_rel_user:read',
'user_subscriptions:sessions', 'user_subscriptions:sessions',
'session:basic',
])] ])]
#[MaxDepth(1)] #[MaxDepth(1)]
#[ORM\ManyToOne(targetEntity: Course::class, cascade: ['persist'], inversedBy: 'sessionRelCourseRelUsers')] #[ORM\ManyToOne(targetEntity: Course::class, cascade: ['persist'], inversedBy: 'sessionRelCourseRelUsers')]

@ -76,7 +76,7 @@ class SessionRelUser
protected ?Session $session = null; protected ?Session $session = null;
#[Assert\NotNull] #[Assert\NotNull]
#[Groups(['session_rel_user:read', 'session:item:read', 'user_subscriptions:sessions'])] #[Groups(['session_rel_user:read', 'session:item:read', 'user_subscriptions:sessions', 'session:basic'])]
#[ORM\ManyToOne(targetEntity: User::class, cascade: ['persist'], inversedBy: 'sessionsRelUser')] #[ORM\ManyToOne(targetEntity: User::class, cascade: ['persist'], inversedBy: 'sessionsRelUser')]
#[ORM\JoinColumn(name: 'user_id', referencedColumnName: 'id', onDelete: 'CASCADE')] #[ORM\JoinColumn(name: 'user_id', referencedColumnName: 'id', onDelete: 'CASCADE')]
protected User $user; protected User $user;

Loading…
Cancel
Save