PluginProbe
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses / 2.6.01
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses v2.6.01
2.11.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 All 78 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.01, at Modules/Course/Http/Controllers/CourseController.php

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