77namespace Chamilo \CoreBundle \Security \Authorization \Voter ;
88
99use Chamilo \CoreBundle \Entity \Course ;
10+ use Chamilo \CoreBundle \Entity \SequenceResource ;
1011use Chamilo \CoreBundle \Entity \Session ;
1112use Chamilo \CoreBundle \Entity \User ;
13+ use Chamilo \CoreBundle \Exception \NotAllowedException ;
1214use Doctrine \ORM \EntityManagerInterface ;
1315use Symfony \Bundle \SecurityBundle \Security ;
1416use Symfony \Component \HttpFoundation \RequestStack ;
1517use Symfony \Component \Security \Core \Authentication \Token \TokenInterface ;
1618use Symfony \Component \Security \Core \Authorization \Voter \Voter ;
1719use Symfony \Component \Security \Core \User \UserInterface ;
20+ use Symfony \Contracts \Translation \TranslatorInterface ;
1821
1922/**
2023 * @extends Voter<'VIEW'|'EDIT'|'DELETE', Course>
@@ -30,6 +33,7 @@ class CourseVoter extends Voter
3033
3134 public function __construct (
3235 private readonly Security $ security ,
36+ private readonly TranslatorInterface $ translator ,
3337 RequestStack $ requestStack ,
3438 EntityManagerInterface $ entityManager
3539 ) {
@@ -58,10 +62,6 @@ protected function voteOnAttribute(string $attribute, $subject, TokenInterface $
5862 {
5963 /** @var User $user */
6064 $ user = $ token ->getUser ();
61- // Anons can enter a course depending on the course visibility.
62- /*if (!$user instanceof UserInterface) {
63- return false;
64- }*/
6565
6666 // Admins have access to everything.
6767 if ($ this ->security ->isGranted ('ROLE_ADMIN ' )) {
@@ -93,9 +93,15 @@ protected function voteOnAttribute(string $attribute, $subject, TokenInterface $
9393 // "Open to the world" no need to check if user is registered or if user exists.
9494 // Course::OPEN_WORLD
9595 if ($ course ->isPublic ()) {
96- /*$user->addRole(ResourceNodeVoter::ROLE_CURRENT_COURSE_STUDENT);
97- $token->setUser($user);*/
98-
96+ if ($ this ->isStudent ($ user , $ course , $ session )) {
97+ if ($ this ->isCourseLockedForUser ($ user , $ course , $ session ?->getId() ?? 0 )) {
98+ throw new NotAllowedException (
99+ $ this ->translator ->trans ('This course is locked. You must complete the prerequisite(s) first. ' ),
100+ 'warning ' ,
101+ 403
102+ );
103+ }
104+ }
99105 return true ;
100106 }
101107
@@ -106,20 +112,16 @@ protected function voteOnAttribute(string $attribute, $subject, TokenInterface $
106112
107113 // If user is logged in and is open platform, allow access.
108114 if (Course::OPEN_PLATFORM === $ course ->getVisibility ()) {
109- $ user ->addRole (ResourceNodeVoter::ROLE_CURRENT_COURSE_STUDENT );
110-
111- if ($ course ->hasUserAsTeacher ($ user )) {
112- $ user ->addRole (ResourceNodeVoter::ROLE_CURRENT_COURSE_TEACHER );
115+ if ($ this ->isStudent ($ user , $ course , $ session )) {
116+ if ($ this ->isCourseLockedForUser ($ user , $ course , $ session ?->getId() ?? 0 )) {
117+ throw new NotAllowedException (
118+ $ this ->translator ->trans ('This course is locked. You must complete the prerequisite(s) first. ' ),
119+ 'warning ' ,
120+ 403
121+ );
122+ }
113123 }
114124
115- $ token ->setUser ($ user );
116-
117- return true ;
118- }
119-
120- // Course::REGISTERED
121- // User must be subscribed in the course no matter if is teacher/student
122- if ($ course ->hasSubscriptionByUser ($ user )) {
123125 $ user ->addRole (ResourceNodeVoter::ROLE_CURRENT_COURSE_STUDENT );
124126
125127 if ($ course ->hasUserAsTeacher ($ user )) {
@@ -137,23 +139,45 @@ protected function voteOnAttribute(string $attribute, $subject, TokenInterface $
137139 $ userIsCourseCoach = $ session ->hasCourseCoachInCourse ($ user , $ course );
138140 $ userIsStudent = $ session ->hasUserInCourse ($ user , $ course , Session::STUDENT );
139141
140- if ($ userIsGeneralCoach ) {
142+ if ($ userIsGeneralCoach || $ userIsCourseCoach ) {
141143 $ user ->addRole (ResourceNodeVoter::ROLE_CURRENT_COURSE_SESSION_TEACHER );
142-
143144 return true ;
144145 }
145146
146- if ($ userIsCourseCoach ) {
147- $ user ->addRole (ResourceNodeVoter::ROLE_CURRENT_COURSE_SESSION_TEACHER );
147+ if ($ userIsStudent ) {
148+ $ user ->addRole (ResourceNodeVoter::ROLE_CURRENT_COURSE_SESSION_STUDENT );
149+ if ($ this ->isCourseLockedForUser ($ user , $ course , $ session ->getId ())) {
150+ throw new NotAllowedException (
151+ $ this ->translator ->trans ('This course is locked. You must complete the prerequisite(s) first. ' ),
152+ 'warning ' ,
153+ 403
154+ );
155+ }
148156
149157 return true ;
150158 }
159+ }
151160
152- if ($ userIsStudent ) {
153- $ user ->addRole (ResourceNodeVoter::ROLE_CURRENT_COURSE_SESSION_STUDENT );
161+ // Course::REGISTERED
162+ // User must be subscribed in the course no matter if is teacher/student
163+ if ($ course ->hasSubscriptionByUser ($ user )) {
164+ $ user ->addRole (ResourceNodeVoter::ROLE_CURRENT_COURSE_STUDENT );
154165
155- return true ;
166+ if ($ course ->hasUserAsTeacher ($ user )) {
167+ $ user ->addRole (ResourceNodeVoter::ROLE_CURRENT_COURSE_TEACHER );
168+ }
169+
170+ if ($ this ->isCourseLockedForUser ($ user , $ course )) {
171+ throw new NotAllowedException (
172+ $ this ->translator ->trans ('This course is locked. You must complete the prerequisite(s) first. ' ),
173+ 'warning ' ,
174+ 403
175+ );
156176 }
177+
178+ $ token ->setUser ($ user );
179+
180+ return true ;
157181 }
158182
159183 break ;
@@ -173,4 +197,39 @@ protected function voteOnAttribute(string $attribute, $subject, TokenInterface $
173197
174198 return false ;
175199 }
200+
201+ /**
202+ * Checks whether the given course is locked for the user
203+ * due to unmet prerequisite sequences.
204+ */
205+ private function isCourseLockedForUser (User $ user , Course $ course , int $ sessionId = 0 ): bool
206+ {
207+ $ sequenceRepo = $ this ->entityManager ->getRepository (SequenceResource::class);
208+
209+ $ sequences = $ sequenceRepo ->getRequirements (
210+ $ course ->getId (),
211+ SequenceResource::COURSE_TYPE
212+ );
213+
214+ if (empty ($ sequences )) {
215+ return false ;
216+ }
217+
218+ $ statusList = $ sequenceRepo ->checkRequirementsForUser (
219+ $ sequences ,
220+ SequenceResource::COURSE_TYPE ,
221+ $ user ->getId ()
222+ );
223+
224+ return !$ sequenceRepo ->checkSequenceAreCompleted ($ statusList );
225+ }
226+
227+ private function isStudent (User $ user , Course $ course , ?Session $ session ): bool
228+ {
229+ if ($ session ) {
230+ return $ session ->hasUserInCourse ($ user , $ course , Session::STUDENT );
231+ }
232+
233+ return $ course ->hasUserAsStudent ($ user );
234+ }
176235}
0 commit comments