PluginProbe
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses / 2.6.0
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses v2.6.0
2.10.0 2.10.01 2.9.1 2.9.0 2.8.1 2.8.0 2.7.7 2.7.5 2.7.0 2.6.01 2.6.0 2.5.0 2.4.01 trunk 1.0.90 1.0.91 1.0.92 1.0.93 1.0.94 1.0.95 1.0.96 1.0.97 1.0.98 1.0.99 1.1.0 All 77 releases
fluent-community / Modules / Course / Http / Controllers / CourseController.php

CourseController.php in FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses 2.6.0, at Modules/Course/Http/Controllers/CourseController.php

482 lines 18.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentCommunity\Modules\Course\Http\Controllers;
4
5 use FluentCommunity\App\Http\Controllers\Controller;
6 use FluentCommunity\App\Models\SpaceUserPivot;
7 use FluentCommunity\App\Services\Helper;
8 use FluentCommunity\App\Services\LockscreenService;
9 use FluentCommunity\Framework\Http\Request\Request;
10 use FluentCommunity\Framework\Support\Arr;
11 use FluentCommunity\Modules\Course\Model\Course;
12 use FluentCommunity\Modules\Course\Model\CourseLesson;
13 use FluentCommunity\Modules\Course\Model\CourseTopic;
14 use FluentCommunity\App\Services\FeedsHelper;
15 use FluentCommunity\Modules\Course\Services\CourseHelper;
16
17 class CourseController extends Controller
18 {
19 public function getCourses(Request $request)
20 {
21 $user = $this->getUser();
22 $isCourseCreator = $user && $user->hasCourseCreatorAccess();
23 $enrolledIds = CourseHelper::getEnrolledCourseIds();
24
25 $search = $request->getSafe('search');
26 $topicSlug = $request->getSafe('topic_slug');
27 $isEnrolled = $request->getSafe('type') == 'enrolled';
28 $sortBy = $request->getSafe('sort_by', 'sanitize_text_field', 'alphabetical');
29
30 $courses = Course::searchBy($search)
31 ->where(function ($q) use ($isCourseCreator, $isEnrolled, $user) {
32 if ($isCourseCreator && !$isEnrolled) {
33 $q->where('status', 'published')
34 ->orWhere('created_by', $user->ID);
35 } else {
36 $q->where('status', 'published');
37 }
38 })
39 ->byPostTopic($topicSlug)
40 ->where(function ($q) use ($enrolledIds) {
41 $q->whereIn('privacy', ['public', 'private']);
42 $q->orWhereIn('id', $enrolledIds);
43 })
44 ->when($isEnrolled, function ($q) use ($enrolledIds) {
45 $q->whereIn('id', $enrolledIds);
46 })
47 ->when($sortBy == 'alphabetical', function ($q) {
48 $q->orderBy('title', 'ASC');
49 })
50 ->when($sortBy == 'oldest', function ($q) {
51 $q->orderBy('created_at', 'ASC');
52 })
53 ->when($sortBy == 'latest', function ($q) {
54 $q->orderBy('created_at', 'DESC');
55 })
56 ->paginate();
57
58 foreach ($courses as $course) {
59 $course->isEnrolled = CourseHelper::isEnrolled($course->id);
60 if ($course->isEnrolled) {
61 $course->progress = CourseHelper::getCourseProgress($course->id);
62 }
63
64 if (!$course->cover_photo) {
65 $course->cover_photo = FLUENT_COMMUNITY_PLUGIN_URL . 'assets/images/course-placeholder.jpg';
66 }
67
68 $course->sectionsCount = CourseTopic::where('space_id', $course->id)->count();
69 $course->lessonsCount = CourseLesson::where('space_id', $course->id)->count();
70 if (Arr::get($course->settings, 'hide_members_count') != 'yes') {
71 $course->studentsCount = SpaceUserPivot::where('space_id', $course->id)->count();
72 } else {
73 $course->studentsCount = 0;
74 }
75 }
76
77 $data = [
78 'courses' => $courses,
79 'course_categories' => $request->get('with_categories') ? CourseHelper::getCourseCategories() : []
80 ];
81
82 return apply_filters('fluent_community/courses_api_response', $data, $request->all());
83 }
84
85 public function getCourse(Request $request, $courseId)
86 {
87 $user = $this->getUser();
88
89 $course = Course::findOrFail($courseId);
90
91 $isCourseCreator = $course->isCourseAdmin($user);
92
93 if ($course->status !== 'published' && !$isCourseCreator) {
94 return $this->sendError([
95 'message' => __('Course not found. maybe the course is not published yet!', 'fluent-community')
96 ]);
97 }
98
99 $intendtedLessonSlug = $request->get('intended_lesson_slug');
100
101 $data = $this->processCourse($course, $isCourseCreator, $intendtedLessonSlug);
102
103 return apply_filters('fluent_community/get_course_api_response', $data, $request->all());
104 }
105
106 public function getCourseBySlug(Request $request, $slug)
107 {
108 $user = $this->getUser();
109
110 $course = Course::where('slug', $slug)->firstOrFail();
111
112 $isCourseCreator = $course->isCourseAdmin($user);
113
114 if ($course->status !== 'published' && !$isCourseCreator) {
115 return $this->sendError([
116 'message' => __('Course not found. maybe the course is not published yet!', 'fluent-community')
117 ]);
118 }
119
120 $hideInstructorView = Arr::get($course->settings, 'hide_instructor_view') == 'yes';
121 $showStudentCount = Arr::get($course->settings, 'show_instructor_students_count') == 'yes';
122 if (!$hideInstructorView) {
123 $course->load(['creator']);
124 if ($course->creator) {
125 $creatorCourseIds = Course::where('created_by', $course->creator->user_id)->pluck('id');
126 $course->creator->total_courses = count($creatorCourseIds);
127 if ($showStudentCount) {
128 $course->creator->total_students = SpaceUserPivot::whereIn('space_id', $creatorCourseIds)->distinct('user_id')->count('user_id');
129 }
130 if ($course->creator->short_description) {
131 $course->creator->short_description_rendered = wp_kses_post(FeedsHelper::mdToHtml($course->creator->short_description));
132 }
133 }
134 }
135
136 $intendtedLessonSlug = $request->get('intended_lesson_slug');
137
138 $data = $this->processCourse($course, $isCourseCreator, $intendtedLessonSlug);
139
140 return apply_filters('fluent_community/course_api_response', $data, $request->all());
141 }
142
143 public function getLessonBySlug(Request $request, $courseSlug, $lessonSlug)
144 {
145 $user = $this->getUser();
146
147 $course = Course::where('slug', $courseSlug)->firstOrFail();
148
149 $isCourseCreator = $course->isCourseAdmin($user);
150
151 if ($course->status !== 'published' && !$isCourseCreator) {
152 return $this->sendError([
153 'message' => __('Course not found. maybe the course is not published yet!', 'fluent-community')
154 ]);
155 }
156
157 $lesson = CourseLesson::where('space_id', $course->id)
158 ->where('slug', $lessonSlug)
159 ->when(!$isCourseCreator, function ($q) {
160 $q->where('status', 'published');
161 })
162 ->with(['topic'])
163 ->firstOrFail();
164
165 if (!$lesson || !$lesson->topic) {
166 return $this->sendError([
167 'message' => __('Lesson not found. maybe the lesson is not published yet!', 'fluent-community')
168 ]);
169 }
170
171 $enrollment = CourseHelper::getCourseEnrollment($course->id);
172
173 if ($course->privacy == 'secret' && !$enrollment && !$isCourseCreator) {
174 return $this->sendError([
175 'message' => __('You must need to be invited to view this course', 'fluent-community')
176 ]);
177 }
178
179 $section = $lesson->topic;
180 $hasAcessSectionAcess = !!$enrollment;
181 $unlockDate = '';
182 $courseType = $course->getCourseType();
183 $hasPublicViewAccess = $course->privacy == 'public' && Arr::get($course->settings, 'public_lesson_view') == 'yes' && $courseType == 'self_paced';
184 if ($hasAcessSectionAcess) {
185 $unlockDate = CourseHelper::getSectionAccessDate($section, $courseType, $enrollment);
186 if ($unlockDate && strtotime($unlockDate) > current_time('timestamp')) {
187 $hasAcessSectionAcess = false;
188 } else {
189 $unlockDate = null;
190 }
191 }
192
193 $initialCanView = $hasAcessSectionAcess || $isCourseCreator || $hasPublicViewAccess;
194 $accessInfo = CourseHelper::resolveLessonAccess($initialCanView, $lesson, $course, $this->getUser(), [
195 'enrollment' => $enrollment,
196 'is_admin' => $isCourseCreator,
197 'has_section_access' => $hasAcessSectionAcess,
198 'has_public_access' => $hasPublicViewAccess,
199 ]);
200 $canViewLesson = $accessInfo['can_view'];
201 $lockType = $accessInfo['lock_type'];
202
203 $formattedLesson = CourseHelper::formatLessonData($course, $lesson, $user, [
204 'can_view' => $canViewLesson,
205 'parse_content' => $canViewLesson,
206 'is_locked' => !$canViewLesson && !$isCourseCreator,
207 'unclock_date' => $unlockDate,
208 'lock_type' => $lockType
209 ]);
210
211 $data = [
212 'lesson' => $formattedLesson
213 ];
214
215 return apply_filters('fluent_community/course_lesson_api_response', $data, $request->all());
216 }
217
218 private function processCourse(Course $course, $isCourseCreator, $intendtedLessonSlug = '')
219 {
220 $sections = CourseTopic::where('space_id', $course->id)
221 ->orderBy('priority', 'ASC')
222 ->with(['lessons' => function ($query) use ($isCourseCreator) {
223 if (!$isCourseCreator) {
224 $query->where('status', 'published');
225 }
226 }])
227 ->get();
228
229 $enrollment = CourseHelper::getCourseEnrollment($course->id);
230
231 $currentUser = Helper::getCurrentUser();
232
233 if ($course->privacy == 'private' && !$enrollment) {
234 $course->lockscreen_config = LockscreenService::getLockscreenConfig($course, null, true);
235 }
236
237 if ($course->privacy == 'secret' && !$enrollment && !$isCourseCreator) {
238 return $this->sendError([
239 'message' => __('You must need to be invited to view this course', 'fluent-community')
240 ]);
241 }
242
243 $course->is_course_admin = $isCourseCreator;
244
245 $course = apply_filters('fluent_community/course/processed', $course, [
246 'is_enrolled' => !!$enrollment,
247 ]);
248
249 $courseSettings = $course->settings;
250 if (Arr::get($courseSettings, 'course_details')) {
251 $courseSettings['course_details_rendered'] = wp_kses_post(FeedsHelper::mdToHtml($courseSettings['course_details']));
252 $course->settings = $courseSettings;
253 }
254
255 $courseType = $course->getCourseType();
256
257 $hasPublicViewAccess = $course->privacy == 'public' && Arr::get($course->settings, 'public_lesson_view') == 'yes' && $courseType == 'self_paced';
258
259 $formattedSections = [];
260
261 foreach ($sections as $section) {
262 if ($section->lessons->isEmpty()) {
263 continue;
264 }
265
266 $hasAcessSectionAcess = !!$enrollment;
267 $unlockDate = '';
268
269 if ($hasAcessSectionAcess) {
270 $unlockDate = CourseHelper::getSectionAccessDate($section, $courseType, $enrollment);
271 if ($unlockDate && strtotime($unlockDate) > current_time('timestamp')) {
272 $hasAcessSectionAcess = false;
273 } else {
274 $unlockDate = null;
275 }
276 }
277
278 $sectionCtx = [
279 'enrollment' => $enrollment,
280 'is_admin' => $isCourseCreator,
281 'has_section_access' => $hasAcessSectionAcess,
282 'has_public_access' => $hasPublicViewAccess,
283 ];
284
285 $formattedLessons = [];
286 foreach ($section->lessons as $lesson) {
287 $initialCanView = $hasAcessSectionAcess || $isCourseCreator || $hasPublicViewAccess;
288 $accessInfo = CourseHelper::resolveLessonAccess($initialCanView, $lesson, $course, $this->getUser(), $sectionCtx);
289 $canViewLesson = $accessInfo['can_view'];
290 $lockType = $accessInfo['lock_type'];
291
292 $parseContent = $intendtedLessonSlug === $lesson->slug && $canViewLesson;
293
294 $formattedLesson = CourseHelper::formatLessonData($course, $lesson, $currentUser, [
295 'can_view' => $canViewLesson,
296 'parse_content' => $parseContent,
297 'is_locked' => !$canViewLesson && !$isCourseCreator,
298 'unclock_date' => $unlockDate,
299 'lock_type' => $lockType
300 ]);
301
302 $isLazyLoad = !$parseContent;
303 if(!$canViewLesson && $lockType !== 'sequential') {
304 $isLazyLoad = false;
305 }
306
307 $formattedLesson['lazy_load'] = $isLazyLoad;
308
309 $formattedLessons[] = $formattedLesson;
310 }
311
312 $formattedSections[] = [
313 'id' => $section->id,
314 'title' => $section->title,
315 'lessons' => $formattedLessons,
316 'created_at' => $section->created_at,
317 'slug' => $section->slug,
318 'type' => 'section',
319 'is_locked' => !$hasAcessSectionAcess && !$isCourseCreator,
320 'unlock_date' => $unlockDate
321 ];
322 }
323
324 return [
325 'course' => $course,
326 'sections' => $formattedSections,
327 'track' => CourseHelper::getCourseProgressTrack($course->id)
328 ];
329 }
330
331 public function enrollCourse(Request $request, $courseId)
332 {
333 $user = $this->getUser();
334
335 if (!$user) {
336 return $this->sendError([
337 'message' => __('You need to login to enroll in this course.', 'fluent-community')
338 ]);
339 }
340
341 $course = Course::findOrFail($courseId);
342
343 $isCourseCreator = $course->isCourseAdmin($user);
344
345 if ($course->status !== 'published' && !$isCourseCreator) {
346 return $this->sendError([
347 'message' => __('Course not found. maybe the course is not published yet!', 'fluent-community')
348 ]);
349 }
350
351 if (!$isCourseCreator && $course->privacy != 'public') {
352 return $this->sendError([
353 'message' => __('You are not allowed to enroll in this course.', 'fluent-community')
354 ]);
355 }
356
357 $enrolled = CourseHelper::enrollCourse($course, get_current_user_id());
358
359 if (!$enrolled) {
360 return [
361 'message' => __('You are already enrolled in this course.', 'fluent-community'),
362 'track' => CourseHelper::getCourseProgressTrack($courseId)
363 ];
364 }
365
366 return [
367 'message' => __('You have been enrolled in this course.', 'fluent-community'),
368 'track' => CourseHelper::getCourseProgressTrack($courseId)
369 ];
370 }
371
372 public function updateCompletionLesson(Request $request, $courseId, $lessonId)
373 {
374 $course = Course::findOrFail($courseId);
375
376 if ($course->status != 'published') {
377 return $this->sendError([
378 'message' => __('Course is not in published state.', 'fluent-community')
379 ]);
380 }
381
382
383 if (!CourseHelper::isEnrolled($courseId)) {
384 return $this->sendError([
385 'message' => __('Please enroll this course first', 'fluent-community')
386 ]);
387 }
388
389 $lesson = CourseLesson::where('space_id', $courseId)
390 ->where('status', 'published')
391 ->where('id', $lessonId)
392 ->first();
393
394 if (!$lesson) {
395 return $this->sendError([
396 'message' => __('Lesson is not on published state.', 'fluent-community')
397 ]);
398 }
399
400 $state = $request->get('state');
401
402 if (!in_array($state, ['completed', 'incomplete'])) {
403 return $this->sendError([
404 'message' => __('Invalid state.', 'fluent-community')
405 ]);
406 }
407
408 $isAllowed = apply_filters('fluent_community/is_allowed_to_complete_lesson', true, $lesson);
409
410 if (!$isAllowed) {
411 return $this->sendError([
412 'message' => __('You are not allowed to complete this lesson.', 'fluent-community')
413 ]);
414 }
415
416 CourseHelper::updateLessonCompletion($lesson, get_current_user_id(), $state);
417 $track = CourseHelper::getCourseProgressTrack($courseId);
418 $isCompleted = $track['progress'] == 100;
419
420 if ($isCompleted) {
421 CourseHelper::completeCourse($course, $lesson, get_current_user_id());
422 }
423
424 return [
425 'message' => __('Lesson completion state updated.', 'fluent-community'),
426 'track' => $track,
427 'is_completed' => $isCompleted
428 ];
429 }
430
431 public function getAllCourses(Request $request)
432 {
433 $user = $this->getUser();
434 $isCourseCreator = $user && $user->hasCourseCreatorAccess();
435
436 $courses = Course::where(function ($q) use ($isCourseCreator, $user) {
437 if ($isCourseCreator) {
438 $q->where('status', 'published')
439 ->orWhere('created_by', $user->ID);
440 } else {
441 $q->where('status', 'published');
442 }
443 })
444 ->with(['creator'])
445 ->paginate();
446
447 $formattedCourses = [];
448 foreach ($courses as $course) {
449 $course->sectionsCount = CourseTopic::where('space_id', $course->id)->count();
450 $course->lessonsCount = CourseLesson::where('space_id', $course->id)->count();
451
452 if (Arr::get($course->settings, 'hide_members_count') != 'yes') {
453 $course->studentsCount = SpaceUserPivot::where('space_id', $course->id)->count();
454 } else {
455 $course->studentsCount = 0;
456 }
457
458 $showInstructorView = Arr::get($course->settings, 'hide_instructor_view') != 'yes';
459 $showStudentCount = Arr::get($course->settings, 'show_instructor_students_count') == 'yes';
460 if ($showInstructorView) {
461 if ($course->creator) {
462 $creatorCourseIds = Course::where('created_by', $course->creator->user_id)->pluck('id');
463 $course->creator->total_courses = count($creatorCourseIds);
464 if ($showStudentCount) {
465 $course->creator->total_students = SpaceUserPivot::whereIn('space_id', $creatorCourseIds)->distinct('user_id')->count('user_id');
466 }
467 if ($course->creator->short_description) {
468 $course->creator->short_description_rendered = wp_kses_post(FeedsHelper::mdToHtml($course->creator->short_description));
469 }
470 }
471 }
472
473 $formattedCourses[] = $this->processCourse($course, $course->isCourseAdmin($user));
474 }
475
476 return apply_filters('fluent_community/all_courses_api_response', [
477 'courses' => $formattedCourses,
478 'total' => $courses->total()
479 ], $request->all());
480 }
481 }
482