PluginProbe
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses / 2.11.0
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses v2.11.0
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
← All changes | Modules/Course/Http/Controllers/CourseController.php +123 -9 2.6.02.11.0 View file →
@@ -12,8 +12,9 @@
12 12 use FluentCommunity\Modules\Course\Model\CourseLesson;
13 13 use FluentCommunity\Modules\Course\Model\CourseTopic;
14 14 use FluentCommunity\App\Services\FeedsHelper;
15 15 use FluentCommunity\Modules\Course\Services\CourseHelper;
16 +use FluentCommunity\Modules\Course\Services\LessonVideoGateService;
16 17
17 18 class CourseController extends Controller
18 19 {
19 20 public function getCourses(Request $request)
@@ -71,8 +72,10 @@
71 72 $course->studentsCount = SpaceUserPivot::where('space_id', $course->id)->count();
72 73 } else {
73 74 $course->studentsCount = 0;
74 75 }
76 +
77 + do_action_ref_array('fluent_community/course', [&$course]);
75 78 }
76 79
77 80 $data = [
78 81 'courses' => $courses,
@@ -86,8 +89,9 @@
86 89 {
87 90 $user = $this->getUser();
88 91
89 92 $course = Course::findOrFail($courseId);
93 + /** @var Course $course */
90 94
91 95 $isCourseCreator = $course->isCourseAdmin($user);
92 96
93 97 if ($course->status !== 'published' && !$isCourseCreator) {
@@ -107,8 +111,9 @@
107 111 {
108 112 $user = $this->getUser();
109 113
110 114 $course = Course::where('slug', $slug)->firstOrFail();
115 + /** @var Course $course */
111 116
112 117 $isCourseCreator = $course->isCourseAdmin($user);
113 118
114 119 if ($course->status !== 'published' && !$isCourseCreator) {
@@ -143,8 +148,9 @@
143 148 public function getLessonBySlug(Request $request, $courseSlug, $lessonSlug)
144 149 {
145 150 $user = $this->getUser();
146 151
152 + /** @var Course $course */
147 153 $course = Course::where('slug', $courseSlug)->firstOrFail();
148 154
149 155 $isCourseCreator = $course->isCourseAdmin($user);
150 156
@@ -203,9 +209,9 @@
203 209 $formattedLesson = CourseHelper::formatLessonData($course, $lesson, $user, [
204 210 'can_view' => $canViewLesson,
205 211 'parse_content' => $canViewLesson,
206 212 'is_locked' => !$canViewLesson && !$isCourseCreator,
207 - 'unclock_date' => $unlockDate,
213 + 'unlock_date' => $unlockDate,
208 214 'lock_type' => $lockType
209 215 ]);
210 216
211 217 $data = [
@@ -218,8 +224,9 @@
218 224 private function processCourse(Course $course, $isCourseCreator, $intendtedLessonSlug = '')
219 225 {
220 226 $sections = CourseTopic::where('space_id', $course->id)
221 227 ->orderBy('priority', 'ASC')
228 + ->orderBy('id', 'ASC')
222 229 ->with(['lessons' => function ($query) use ($isCourseCreator) {
223 230 if (!$isCourseCreator) {
224 231 $query->where('status', 'published');
225 232 }
@@ -240,8 +247,9 @@
240 247 ]);
241 248 }
242 249
243 250 $course->is_course_admin = $isCourseCreator;
251 + $course->can_self_enroll = $isCourseCreator || ($course->status === 'published' && $course->privacy === 'public');
244 252
245 253 $course = apply_filters('fluent_community/course/processed', $course, [
246 254 'is_enrolled' => !!$enrollment,
247 255 ]);
@@ -294,9 +302,9 @@
294 302 $formattedLesson = CourseHelper::formatLessonData($course, $lesson, $currentUser, [
295 303 'can_view' => $canViewLesson,
296 304 'parse_content' => $parseContent,
297 305 'is_locked' => !$canViewLesson && !$isCourseCreator,
298 - 'unclock_date' => $unlockDate,
306 + 'unlock_date' => $unlockDate,
299 307 'lock_type' => $lockType
300 308 ]);
301 309
302 310 $isLazyLoad = !$parseContent;
@@ -338,8 +346,9 @@
338 346 ]);
339 347 }
340 348
341 349 $course = Course::findOrFail($courseId);
350 + /** @var Course $course */
342 351
343 352 $isCourseCreator = $course->isCourseAdmin($user);
344 353
345 354 if ($course->status !== 'published' && !$isCourseCreator) {
@@ -404,14 +413,21 @@
404 413 'message' => __('Invalid state.', 'fluent-community')
405 414 ]);
406 415 }
407 416
408 - $isAllowed = apply_filters('fluent_community/is_allowed_to_complete_lesson', true, $lesson);
417 + $isAllowed = apply_filters('fluent_community/is_allowed_to_complete_lesson', true, $lesson, $state);
409 418
410 419 if (!$isAllowed) {
411 - return $this->sendError([
420 + $errorData = [
412 421 'message' => __('You are not allowed to complete this lesson.', 'fluent-community')
413 - ]);
422 + ];
423 +
424 + if (LessonVideoGateService::isCompletionBlockedFor($lesson, Helper::getCurrentUser())) {
425 + $errorData['code'] = 'video_watch_required';
426 + $errorData['threshold'] = LessonVideoGateService::getThreshold($lesson);
427 + }
428 +
429 + return $this->sendError($errorData);
414 430 }
415 431
416 432 CourseHelper::updateLessonCompletion($lesson, get_current_user_id(), $state);
417 433 $track = CourseHelper::getCourseProgressTrack($courseId);
@@ -427,14 +443,100 @@
427 443 'is_completed' => $isCompleted
428 444 ];
429 445 }
430 446
447 + public function markLessonVideoWatched(Request $request, $courseId, $lessonId)
448 + {
449 + $course = Course::findOrFail($courseId);
450 +
451 + if ($course->status != 'published') {
452 + return $this->sendError([
453 + 'message' => __('Course is not in published state.', 'fluent-community')
454 + ]);
455 + }
456 +
457 + if (!CourseHelper::isEnrolled($courseId)) {
458 + return $this->sendError([
459 + 'message' => __('Please enroll this course first', 'fluent-community')
460 + ]);
461 + }
462 +
463 + $lesson = CourseLesson::where('space_id', $courseId)
464 + ->where('status', 'published')
465 + ->where('id', $lessonId)
466 + ->first();
467 +
468 + if (!$lesson) {
469 + return $this->sendError([
470 + 'message' => __('Lesson is not on published state.', 'fluent-community')
471 + ]);
472 + }
473 +
474 + if (!LessonVideoGateService::isGatedLesson($lesson)) {
475 + return [
476 + 'message' => __('This lesson does not require video completion.', 'fluent-community'),
477 + 'is_gated' => false,
478 + 'watched' => false
479 + ];
480 + }
481 +
482 + $watchedPercent = min(100, absint($request->get('watched_percent')));
483 + $threshold = LessonVideoGateService::getThreshold($lesson);
484 +
485 + if ($watchedPercent < $threshold) {
486 + return $this->sendError([
487 + /* translators: %d is the watched percentage required to complete the lesson */
488 + 'message' => sprintf(__('Please watch at least %d%% of the video.', 'fluent-community'), $threshold)
489 + ]);
490 + }
491 +
492 + LessonVideoGateService::markWatched($lesson, get_current_user_id());
493 +
494 + return [
495 + 'message' => __('Video watch progress has been recorded.', 'fluent-community'),
496 + 'is_gated' => true,
497 + 'watched' => true
498 + ];
499 + }
500 +
501 + public function resetMyProgress(Request $request, $courseId)
502 + {
503 + $course = Course::findOrFail($courseId);
504 +
505 + if ($course->status !== 'published') {
506 + return $this->sendError([
507 + 'message' => __('Course is not in published state.', 'fluent-community')
508 + ]);
509 + }
510 +
511 + if (!CourseHelper::isEnrolled($courseId)) {
512 + return $this->sendError([
513 + 'message' => __('Please enroll this course first', 'fluent-community')
514 + ]);
515 + }
516 +
517 + if (!CourseHelper::resetCourseProgress($courseId, get_current_user_id())) {
518 + return $this->sendError([
519 + 'message' => __('Your course progress could not be reset. Please try again.', 'fluent-community')
520 + ]);
521 + }
522 +
523 + return [
524 + 'message' => __('Your course progress has been reset.', 'fluent-community'),
525 + 'track' => [
526 + 'completed_lessons' => [],
527 + 'isEnrolled' => true,
528 + 'progress' => 0
529 + ]
530 + ];
531 + }
532 +
431 533 public function getAllCourses(Request $request)
432 534 {
433 535 $user = $this->getUser();
434 536 $isCourseCreator = $user && $user->hasCourseCreatorAccess();
435 537
436 - $courses = Course::where(function ($q) use ($isCourseCreator, $user) {
538 + $coursesQuery = Course::where(function ($q) use ($isCourseCreator, $user) {
437 539 if ($isCourseCreator) {
438 540 $q->where('status', 'published')
439 541 ->orWhere('created_by', $user->ID);
440 542 } else {
@@ -439,11 +541,23 @@
439 541 ->orWhere('created_by', $user->ID);
440 542 } else {
441 543 $q->where('status', 'published');
442 544 }
443 - })
444 - ->with(['creator'])
445 - ->paginate();
545 + });
546 +
547 + if (!($user && $user->isCommunityModerator())) {
548 + $userId = get_current_user_id();
549 + $coursesQuery->where(function ($q) use ($userId) {
550 + $q->whereIn('privacy', ['public', 'private'])
551 + ->orWhereHas('space_pivot', function ($sub) use ($userId) {
552 + $sub->where('user_id', $userId)
553 + ->where('role', 'student');
554 + })
555 + ->orWhere('created_by', $userId);
556 + });
557 + }
558 +
559 + $courses = $coursesQuery->with(['creator'])->paginate();
446 560
447 561 $formattedCourses = [];
448 562 foreach ($courses as $course) {
449 563 $course->sectionsCount = CourseTopic::where('space_id', $course->id)->count();