PluginProbe
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses / 2.5.0
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses v2.5.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.5.0, at Modules/Course/Http/Controllers/CourseController.php

478 lines 18.5 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 $courseSettings = $course->settings;
246 if (Arr::get($courseSettings, 'course_details')) {
247 $courseSettings['course_details_rendered'] = wp_kses_post(FeedsHelper::mdToHtml($courseSettings['course_details']));
248 $course->settings = $courseSettings;
249 }
250
251 $courseType = $course->getCourseType();
252
253 $hasPublicViewAccess = $course->privacy == 'public' && Arr::get($course->settings, 'public_lesson_view') == 'yes' && $courseType == 'self_paced';
254
255 $formattedSections = [];
256
257 foreach ($sections as $section) {
258 if ($section->lessons->isEmpty()) {
259 continue;
260 }
261
262 $hasAcessSectionAcess = !!$enrollment;
263 $unlockDate = '';
264
265 if ($hasAcessSectionAcess) {
266 $unlockDate = CourseHelper::getSectionAccessDate($section, $courseType, $enrollment);
267 if ($unlockDate && strtotime($unlockDate) > current_time('timestamp')) {
268 $hasAcessSectionAcess = false;
269 } else {
270 $unlockDate = null;
271 }
272 }
273
274 $sectionCtx = [
275 'enrollment' => $enrollment,
276 'is_admin' => $isCourseCreator,
277 'has_section_access' => $hasAcessSectionAcess,
278 'has_public_access' => $hasPublicViewAccess,
279 ];
280
281 $formattedLessons = [];
282 foreach ($section->lessons as $lesson) {
283 $initialCanView = $hasAcessSectionAcess || $isCourseCreator || $hasPublicViewAccess;
284 $accessInfo = CourseHelper::resolveLessonAccess($initialCanView, $lesson, $course, $this->getUser(), $sectionCtx);
285 $canViewLesson = $accessInfo['can_view'];
286 $lockType = $accessInfo['lock_type'];
287
288 $parseContent = $intendtedLessonSlug === $lesson->slug && $canViewLesson;
289
290 $formattedLesson = CourseHelper::formatLessonData($course, $lesson, $currentUser, [
291 'can_view' => $canViewLesson,
292 'parse_content' => $parseContent,
293 'is_locked' => !$canViewLesson && !$isCourseCreator,
294 'unclock_date' => $unlockDate,
295 'lock_type' => $lockType
296 ]);
297
298 $isLazyLoad = !$parseContent;
299 if(!$canViewLesson && $lockType !== 'sequential') {
300 $isLazyLoad = false;
301 }
302
303 $formattedLesson['lazy_load'] = $isLazyLoad;
304
305 $formattedLessons[] = $formattedLesson;
306 }
307
308 $formattedSections[] = [
309 'id' => $section->id,
310 'title' => $section->title,
311 'lessons' => $formattedLessons,
312 'created_at' => $section->created_at,
313 'slug' => $section->slug,
314 'type' => 'section',
315 'is_locked' => !$hasAcessSectionAcess && !$isCourseCreator,
316 'unlock_date' => $unlockDate
317 ];
318 }
319
320 return [
321 'course' => $course,
322 'sections' => $formattedSections,
323 'track' => CourseHelper::getCourseProgressTrack($course->id)
324 ];
325 }
326
327 public function enrollCourse(Request $request, $courseId)
328 {
329 $user = $this->getUser();
330
331 if (!$user) {
332 return $this->sendError([
333 'message' => __('You need to login to enroll in this course.', 'fluent-community')
334 ]);
335 }
336
337 $course = Course::findOrFail($courseId);
338
339 $isCourseCreator = $course->isCourseAdmin($user);
340
341 if ($course->status !== 'published' && !$isCourseCreator) {
342 return $this->sendError([
343 'message' => __('Course not found. maybe the course is not published yet!', 'fluent-community')
344 ]);
345 }
346
347 if (!$isCourseCreator && $course->privacy != 'public') {
348 return $this->sendError([
349 'message' => __('You are not allowed to enroll in this course.', 'fluent-community')
350 ]);
351 }
352
353 $enrolled = CourseHelper::enrollCourse($course, get_current_user_id());
354
355 if (!$enrolled) {
356 return [
357 'message' => __('You are already enrolled in this course.', 'fluent-community'),
358 'track' => CourseHelper::getCourseProgressTrack($courseId)
359 ];
360 }
361
362 return [
363 'message' => __('You have been enrolled in this course.', 'fluent-community'),
364 'track' => CourseHelper::getCourseProgressTrack($courseId)
365 ];
366 }
367
368 public function updateCompletionLesson(Request $request, $courseId, $lessonId)
369 {
370 $course = Course::findOrFail($courseId);
371
372 if ($course->status != 'published') {
373 return $this->sendError([
374 'message' => __('Course is not in published state.', 'fluent-community')
375 ]);
376 }
377
378
379 if (!CourseHelper::isEnrolled($courseId)) {
380 return $this->sendError([
381 'message' => __('Please enroll this course first', 'fluent-community')
382 ]);
383 }
384
385 $lesson = CourseLesson::where('space_id', $courseId)
386 ->where('status', 'published')
387 ->where('id', $lessonId)
388 ->first();
389
390 if (!$lesson) {
391 return $this->sendError([
392 'message' => __('Lesson is not on published state.', 'fluent-community')
393 ]);
394 }
395
396 $state = $request->get('state');
397
398 if (!in_array($state, ['completed', 'incomplete'])) {
399 return $this->sendError([
400 'message' => __('Invalid state.', 'fluent-community')
401 ]);
402 }
403
404 $isAllowed = apply_filters('fluent_community/is_allowed_to_complete_lesson', true, $lesson);
405
406 if (!$isAllowed) {
407 return $this->sendError([
408 'message' => __('You are not allowed to complete this lesson.', 'fluent-community')
409 ]);
410 }
411
412 CourseHelper::updateLessonCompletion($lesson, get_current_user_id(), $state);
413 $track = CourseHelper::getCourseProgressTrack($courseId);
414 $isCompleted = $track['progress'] == 100;
415
416 if ($isCompleted) {
417 CourseHelper::completeCourse($course, $lesson, get_current_user_id());
418 }
419
420 return [
421 'message' => __('Lesson completion state updated.', 'fluent-community'),
422 'track' => $track,
423 'is_completed' => $isCompleted
424 ];
425 }
426
427 public function getAllCourses(Request $request)
428 {
429 $user = $this->getUser();
430 $isCourseCreator = $user && $user->hasCourseCreatorAccess();
431
432 $courses = Course::where(function ($q) use ($isCourseCreator, $user) {
433 if ($isCourseCreator) {
434 $q->where('status', 'published')
435 ->orWhere('created_by', $user->ID);
436 } else {
437 $q->where('status', 'published');
438 }
439 })
440 ->with(['creator'])
441 ->paginate();
442
443 $formattedCourses = [];
444 foreach ($courses as $course) {
445 $course->sectionsCount = CourseTopic::where('space_id', $course->id)->count();
446 $course->lessonsCount = CourseLesson::where('space_id', $course->id)->count();
447
448 if (Arr::get($course->settings, 'hide_members_count') != 'yes') {
449 $course->studentsCount = SpaceUserPivot::where('space_id', $course->id)->count();
450 } else {
451 $course->studentsCount = 0;
452 }
453
454 $showInstructorView = Arr::get($course->settings, 'hide_instructor_view') != 'yes';
455 $showStudentCount = Arr::get($course->settings, 'show_instructor_students_count') == 'yes';
456 if ($showInstructorView) {
457 if ($course->creator) {
458 $creatorCourseIds = Course::where('created_by', $course->creator->user_id)->pluck('id');
459 $course->creator->total_courses = count($creatorCourseIds);
460 if ($showStudentCount) {
461 $course->creator->total_students = SpaceUserPivot::whereIn('space_id', $creatorCourseIds)->distinct('user_id')->count('user_id');
462 }
463 if ($course->creator->short_description) {
464 $course->creator->short_description_rendered = wp_kses_post(FeedsHelper::mdToHtml($course->creator->short_description));
465 }
466 }
467 }
468
469 $formattedCourses[] = $this->processCourse($course, $course->isCourseAdmin($user));
470 }
471
472 return apply_filters('fluent_community/all_courses_api_response', [
473 'courses' => $formattedCourses,
474 'total' => $courses->total()
475 ], $request->all());
476 }
477 }
478