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

@ -1,6 +1,8 @@
import { storeToRefs } from "pinia"
import { useCidReqStore } from "../store/cidReq"
import api from "../config/api"
import { computed, ref, unref } from "vue"
import { useSecurityStore } from "../store/securityStore"
/**
* @param {boolean} tutor
@ -37,3 +39,45 @@ export async function checkIsAllowedToEdit(
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 glossary from "./glossary"
import { useSecurityStore } from "../store/securityStore"
import securityService from "../services/securityService"
import MyCourseList from "../views/user/courses/List.vue"
import MySessionList from "../views/user/sessions/SessionsCurrent.vue"
import MySessionListPast from "../views/user/sessions/SessionsPast.vue"
@ -44,6 +43,7 @@ import courseService from "../services/courseService"
import catalogueCourses from "./cataloguecourses"
import catalogueSessions from "./cataloguesessions"
import { customVueTemplateEnabled } from "../config/env"
import { useUserSessionSubscription } from "../composables/userPermissions"
const router = createRouter({
history: createWebHistory(),
@ -208,10 +208,12 @@ router.beforeResolve(async (to) => {
await cidReqStore.setCourseAndSessionById(cid, sid)
if (cidReqStore.session) {
const isGeneralCoach = cidReqStore.session.generalCoaches.includes(securityStore.user["@id"])
const isCourseCoach = cidReqStore.session.courseCoaches.includes(securityStore.user["@id"])
const { isGeneralCoach, isCourseCoach } = useUserSessionSubscription()
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")
} else {
securityStore.user.roles.push("ROLE_CURRENT_COURSE_SESSION_STUDENT")

@ -1,7 +1,6 @@
import { defineStore } from "pinia"
import { usePlatformConfig } from "./platformConfig"
import courseService from "../services/courseService"
import { computed, ref } from "vue"
import { ref } from "vue"
import sessionService from "../services/sessionService"
import { useCourseSettings } from "./courseSettingStore"
@ -13,42 +12,6 @@ export const useCidReqStore = defineStore("cidReq", () => {
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 = () => {
course.value = null
session.value = null
@ -113,8 +76,6 @@ export const useCidReqStore = defineStore("cidReq", () => {
session,
group,
userIsCoach,
resetCid,
setCourseAndSessionById,

@ -16,6 +16,17 @@ export const useSecurityStore = defineStore("security", () => {
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 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"))
async function checkSession() {
isLoading.value = true
try {
@ -59,6 +69,7 @@ export const useSecurityStore = defineStore("security", () => {
isLoading,
isAuthenticated,
hasRole,
removeRole,
isStudent,
isStudentBoss,
isHRM,

@ -71,10 +71,6 @@ class IndexController extends BaseController
#[Security("is_granted('ROLE_TEACHER')")]
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')) {
throw $this->createAccessDeniedException();
}

@ -768,15 +768,20 @@ class Session implements ResourceWithAccessUrlInterface, Stringable
return $this;
}
#[Groups(['session:basic'])]
public function getGeneralCoaches(): ReadableCollection
/**
* @return Collection<int, SessionRelUser>
*/
public function getGeneralCoaches(): Collection
{
return $this->getGeneralCoachesSubscriptions()
->map(fn (SessionRelUser $subscription) => $subscription->getUser())
;
}
#[Groups(['user_subscriptions:sessions'])]
/**
* @return Collection<int, SessionRelUser>
*/
#[Groups(['session:basic', 'user_subscriptions:sessions'])]
public function getGeneralCoachesSubscriptions(): Collection
{
$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;
}
#[Groups(['session:basic'])]
public function getCourseCoaches()
/**
* @return Collection<int, SessionRelCourseRelUser>
*/
public function getCourseCoaches(): Collection
{
return $this->getCourseCoachesSubscriptions()
->map(fn (SessionRelCourseRelUser $subscription) => $subscription->getUser())
@ -1316,7 +1323,7 @@ class Session implements ResourceWithAccessUrlInterface, Stringable
/**
* @return Collection<int, SessionRelCourseRelUser>
*/
#[Groups(['user_subscriptions:sessions'])]
#[Groups(['session:basic', 'user_subscriptions:sessions'])]
public function getCourseCoachesSubscriptions(): Collection
{
return $this->getAllUsersFromCourse(self::COURSE_COACH);

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

@ -76,7 +76,7 @@ class SessionRelUser
protected ?Session $session = null;
#[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\JoinColumn(name: 'user_id', referencedColumnName: 'id', onDelete: 'CASCADE')]
protected User $user;

Loading…
Cancel
Save