New_Micro_Learning/backend/app/Modules/Publishing/Http/CoursePublicationController...

142 خطوط
6.5 KiB
PHP

<?php
namespace App\Modules\Publishing\Http;
use App\Http\Controllers\Controller;
use App\Modules\Assignments\Application\AssignmentResolver;
use App\Modules\Assignments\Domain\Assignment;
use App\Modules\Courses\Domain\Course;
use App\Modules\Courses\Domain\CourseVersion;
use App\Modules\Identity\Application\RolePermissions;
use App\Modules\Identity\Domain\Enums\Permission;
use App\Modules\Publishing\Application\CoursePublication;
use App\Modules\Tenancy\Application\TenantContext;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\Rule;
final class CoursePublicationController extends Controller
{
public function __construct(private readonly TenantContext $tenant, private readonly RolePermissions $permissions, private readonly CoursePublication $publication, private readonly AssignmentResolver $resolver) {}
public function readiness(Request $request, string $course, string $version): JsonResponse
{
$this->authorize($request);
return response()->json(['data' => $this->publication->readiness($this->version($course, $version))]);
}
public function configure(Request $request, string $course, string $version): JsonResponse
{
$this->authorize($request);
$data = $request->validate([
'mode' => ['required', Rule::in(['all', 'any'])], 'rules' => ['required', 'array', 'min:1', 'max:8'],
'rules.*.type' => ['required', Rule::in(['all_required_lessons', 'minimum_lesson_percentage', 'assessment_passed', 'minimum_score', 'required_interaction'])],
'rules.*.value' => ['nullable', 'numeric', 'between:0,100'],
]);
$model = $this->publication->configure($this->version($course, $version), $data);
return response()->json(['data' => $this->payload($model)]);
}
public function submitReview(Request $request, string $course, string $version): JsonResponse
{
$this->authorize($request);
return response()->json(['data' => $this->payload($this->publication->submitReview($this->version($course, $version)))]);
}
public function returnDraft(Request $request, string $course, string $version): JsonResponse
{
$this->authorize($request);
return response()->json(['data' => $this->payload($this->publication->returnToDraft($this->version($course, $version)))]);
}
public function publish(Request $request, string $course, string $version): JsonResponse
{
$this->authorize($request);
$data = $request->validate(['reassignMode' => ['required', Rule::in(['none', 'everyone', 'selected'])], 'assignmentIds' => ['nullable', 'array'], 'assignmentIds.*' => ['string']]);
$model = $this->version($course, $version);
$published = $this->publication->publish($model, (string) $request->user()->getKey());
$created = $this->reassign($request, $published, $data['reassignMode'], $data['assignmentIds'] ?? []);
return response()->json(['data' => [...$this->payload($published), 'reassignmentsCreated' => $created]]);
}
public function schedule(Request $request, string $course, string $version): JsonResponse
{
$this->authorize($request);
$data = $request->validate(['scheduledPublishAt' => ['required', 'date', 'after:now'], 'scheduledUnpublishAt' => ['nullable', 'date', 'after:scheduledPublishAt']]);
$model = $this->publication->schedule($this->version($course, $version), new \DateTimeImmutable($data['scheduledPublishAt']), isset($data['scheduledUnpublishAt']) ? new \DateTimeImmutable($data['scheduledUnpublishAt']) : null);
return response()->json(['data' => $this->payload($model)]);
}
public function cancelSchedule(Request $request, string $course, string $version): JsonResponse
{
$this->authorize($request);
return response()->json(['data' => $this->payload($this->publication->cancelSchedule($this->version($course, $version)))]);
}
public function unpublish(Request $request, string $course, string $version): JsonResponse
{
$this->authorize($request);
$this->publication->unpublish($this->version($course, $version));
return response()->json(status: 204);
}
/** @param list<string> $selected */
private function reassign(Request $request, CourseVersion $published, string $mode, array $selected): int
{
if ($mode === 'none' || ! $published->source_version_id) {
return 0;
}
$query = Assignment::query()->where('organization_id', $this->tenant->id())->where('assignable_type', 'course')->where('assignable_id', $published->source_version_id)->where('status', 'active');
if ($mode === 'selected') {
$query->whereIn('id', $selected);
}
return DB::transaction(function () use ($query, $request, $published): int {
$count = 0;
foreach ($query->get() as $source) {
$copy = $source->replicate(['cancelled_at']);
$copy->assignable_id = $published->getKey();
$copy->assigned_by = $request->user()->getKey();
$copy->source = 'version_reassignment';
$copy->save();
$this->resolver->sync($copy);
$count++;
}
return $count;
});
}
private function version(string $course, string $version): CourseVersion
{
Course::query()->where('organization_id', $this->tenant->id())->findOrFail($course);
return CourseVersion::query()->where('organization_id', $this->tenant->id())->where('course_id', $course)->findOrFail($version);
}
/** @return array<string, mixed> */
private function payload(CourseVersion $version): array
{
return [
'id' => $version->getKey(), 'status' => $version->status->value, 'completionRules' => $version->completion_rules,
'reviewSubmittedAt' => $version->review_submitted_at?->toISOString(), 'publishedAt' => $version->published_at?->toISOString(),
'scheduledPublishAt' => $version->scheduled_publish_at?->toISOString(), 'scheduledUnpublishAt' => $version->scheduled_unpublish_at?->toISOString(),
'unpublishedAt' => $version->unpublished_at?->toISOString(), 'taxonomySnapshotCount' => count($version->taxonomy_snapshot ?? []),
];
}
private function authorize(Request $request): void
{
abort_unless($this->permissions->allows($request->user(), Permission::CoursesPublish), 403);
}
}