← All changes
|
Modules/Course/Http/Controllers/CourseController.php
+116
-9
2.6.01
→
2.10.01
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) |
| @@ -88,8 +89,9 @@ | ||
| 88 | 89 | { |
| 89 | 90 | $user = $this->getUser(); |
| 90 | 91 | |
| 91 | 92 | $course = Course::findOrFail($courseId); |
| 93 | + /** @var Course $course */ | |
| 92 | 94 | |
| 93 | 95 | $isCourseCreator = $course->isCourseAdmin($user); |
| 94 | 96 | |
| 95 | 97 | if ($course->status !== 'published' && !$isCourseCreator) { |
| @@ -109,8 +111,9 @@ | ||
| 109 | 111 | { |
| 110 | 112 | $user = $this->getUser(); |
| 111 | 113 | |
| 112 | 114 | $course = Course::where('slug', $slug)->firstOrFail(); |
| 115 | + /** @var Course $course */ | |
| 113 | 116 | |
| 114 | 117 | $isCourseCreator = $course->isCourseAdmin($user); |
| 115 | 118 | |
| 116 | 119 | if ($course->status !== 'published' && !$isCourseCreator) { |
| @@ -145,8 +148,9 @@ | ||
| 145 | 148 | public function getLessonBySlug(Request $request, $courseSlug, $lessonSlug) |
| 146 | 149 | { |
| 147 | 150 | $user = $this->getUser(); |
| 148 | 151 | |
| 152 | + /** @var Course $course */ | |
| 149 | 153 | $course = Course::where('slug', $courseSlug)->firstOrFail(); |
| 150 | 154 | |
| 151 | 155 | $isCourseCreator = $course->isCourseAdmin($user); |
| 152 | 156 | |
| @@ -205,9 +209,9 @@ | ||
| 205 | 209 | $formattedLesson = CourseHelper::formatLessonData($course, $lesson, $user, [ |
| 206 | 210 | 'can_view' => $canViewLesson, |
| 207 | 211 | 'parse_content' => $canViewLesson, |
| 208 | 212 | 'is_locked' => !$canViewLesson && !$isCourseCreator, |
| 209 | - 'unclock_date' => $unlockDate, | |
| 213 | + 'unlock_date' => $unlockDate, | |
| 210 | 214 | 'lock_type' => $lockType |
| 211 | 215 | ]); |
| 212 | 216 | |
| 213 | 217 | $data = [ |
| @@ -220,8 +224,9 @@ | ||
| 220 | 224 | private function processCourse(Course $course, $isCourseCreator, $intendtedLessonSlug = '') |
| 221 | 225 | { |
| 222 | 226 | $sections = CourseTopic::where('space_id', $course->id) |
| 223 | 227 | ->orderBy('priority', 'ASC') |
| 228 | + ->orderBy('id', 'ASC') | |
| 224 | 229 | ->with(['lessons' => function ($query) use ($isCourseCreator) { |
| 225 | 230 | if (!$isCourseCreator) { |
| 226 | 231 | $query->where('status', 'published'); |
| 227 | 232 | } |
| @@ -297,9 +302,9 @@ | ||
| 297 | 302 | $formattedLesson = CourseHelper::formatLessonData($course, $lesson, $currentUser, [ |
| 298 | 303 | 'can_view' => $canViewLesson, |
| 299 | 304 | 'parse_content' => $parseContent, |
| 300 | 305 | 'is_locked' => !$canViewLesson && !$isCourseCreator, |
| 301 | - 'unclock_date' => $unlockDate, | |
| 306 | + 'unlock_date' => $unlockDate, | |
| 302 | 307 | 'lock_type' => $lockType |
| 303 | 308 | ]); |
| 304 | 309 | |
| 305 | 310 | $isLazyLoad = !$parseContent; |
| @@ -341,8 +346,9 @@ | ||
| 341 | 346 | ]); |
| 342 | 347 | } |
| 343 | 348 | |
| 344 | 349 | $course = Course::findOrFail($courseId); |
| 350 | + /** @var Course $course */ | |
| 345 | 351 | |
| 346 | 352 | $isCourseCreator = $course->isCourseAdmin($user); |
| 347 | 353 | |
| 348 | 354 | if ($course->status !== 'published' && !$isCourseCreator) { |
| @@ -407,14 +413,21 @@ | ||
| 407 | 413 | 'message' => __('Invalid state.', 'fluent-community') |
| 408 | 414 | ]); |
| 409 | 415 | } |
| 410 | 416 | |
| 411 | - $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); | |
| 412 | 418 | |
| 413 | 419 | if (!$isAllowed) { |
| 414 | - return $this->sendError([ | |
| 420 | + $errorData = [ | |
| 415 | 421 | 'message' => __('You are not allowed to complete this lesson.', 'fluent-community') |
| 416 | - ]); | |
| 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); | |
| 417 | 430 | } |
| 418 | 431 | |
| 419 | 432 | CourseHelper::updateLessonCompletion($lesson, get_current_user_id(), $state); |
| 420 | 433 | $track = CourseHelper::getCourseProgressTrack($courseId); |
| @@ -430,14 +443,96 @@ | ||
| 430 | 443 | 'is_completed' => $isCompleted |
| 431 | 444 | ]; |
| 432 | 445 | } |
| 433 | 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 | + CourseHelper::resetCourseProgress($courseId, get_current_user_id()); | |
| 518 | + | |
| 519 | + return [ | |
| 520 | + 'message' => __('Your course progress has been reset.', 'fluent-community'), | |
| 521 | + 'track' => [ | |
| 522 | + 'completed_lessons' => [], | |
| 523 | + 'isEnrolled' => true, | |
| 524 | + 'progress' => 0 | |
| 525 | + ] | |
| 526 | + ]; | |
| 527 | + } | |
| 528 | + | |
| 434 | 529 | public function getAllCourses(Request $request) |
| 435 | 530 | { |
| 436 | 531 | $user = $this->getUser(); |
| 437 | 532 | $isCourseCreator = $user && $user->hasCourseCreatorAccess(); |
| 438 | 533 | |
| 439 | - $courses = Course::where(function ($q) use ($isCourseCreator, $user) { | |
| 534 | + $coursesQuery = Course::where(function ($q) use ($isCourseCreator, $user) { | |
| 440 | 535 | if ($isCourseCreator) { |
| 441 | 536 | $q->where('status', 'published') |
| 442 | 537 | ->orWhere('created_by', $user->ID); |
| 443 | 538 | } else { |
| @@ -442,11 +537,23 @@ | ||
| 442 | 537 | ->orWhere('created_by', $user->ID); |
| 443 | 538 | } else { |
| 444 | 539 | $q->where('status', 'published'); |
| 445 | 540 | } |
| 446 | - }) | |
| 447 | - ->with(['creator']) | |
| 448 | - ->paginate(); | |
| 541 | + }); | |
| 542 | + | |
| 543 | + if (!($user && $user->isCommunityModerator())) { | |
| 544 | + $userId = get_current_user_id(); | |
| 545 | + $coursesQuery->where(function ($q) use ($userId) { | |
| 546 | + $q->whereIn('privacy', ['public', 'private']) | |
| 547 | + ->orWhereHas('space_pivot', function ($sub) use ($userId) { | |
| 548 | + $sub->where('user_id', $userId) | |
| 549 | + ->where('role', 'student'); | |
| 550 | + }) | |
| 551 | + ->orWhere('created_by', $userId); | |
| 552 | + }); | |
| 553 | + } | |
| 554 | + | |
| 555 | + $courses = $coursesQuery->with(['creator'])->paginate(); | |
| 449 | 556 | |
| 450 | 557 | $formattedCourses = []; |
| 451 | 558 | foreach ($courses as $course) { |
| 452 | 559 | $course->sectionsCount = CourseTopic::where('space_id', $course->id)->count(); |