user_id ), true ) ) { DomainError::raise( ErrorCode::INVALID, array( 'reason' => 'kind' ) ); } if ( '' !== $rev ) { $current = Services::curriculum_repo()->rev( $course_id ); if ( ! hash_equals( $current, $rev ) ) { $stale = $current; return array( 0, 0, 0 ); } } if ( $section_id > 0 && ! in_array( $section_id, array_map( static fn( $s ): int => $s->id, Services::curriculum_repo()->sections_of_courses( array( $course_id ) )[ $course_id ] ?? array() ), true ) ) { DomainError::raise( ErrorCode::INVALID ); } $title = trim( sanitize_text_field( $title ) ); $title = '' !== $title ? mb_substr( $title, 0, 200 ) : self::default_title( $kind ); if ( in_array( $kind, Kinds::FREE, true ) ) { list( $unit_id, $post_id ) = self::free_unit( $kind, $title, $actor ); } else { $unit_id = (int) apply_filters( 'rooster_lms_course_builder_create_unit', 0, $kind, $title, $course_id, $actor->user_id ); if ( $unit_id <= 0 ) { DomainError::raise( ErrorCode::INVALID, array( 'reason' => 'kind_unavailable' ) ); } $post_id = (int) ( Services::unit_repo()->get_many( array( $unit_id ) )[ $unit_id ]->home_post_id ?? 0 ); } try { $placed = Placer::place( $course_id, array( $unit_id ), $section_id, $position, '', $actor ); } catch ( \Throwable $e ) { if ( $post_id > 0 ) { wp_delete_post( $post_id, true ); } throw $e; } return array( $unit_id, (int) ( $placed[ $unit_id ] ?? 0 ), $post_id ); } public static function standalone( string $kind, string $title, Actor $actor ): array { if ( ! in_array( $kind, Kinds::ALL, true ) && ! in_array( $kind, self::addon_kinds( $actor->user_id ), true ) ) { DomainError::raise( ErrorCode::INVALID, array( 'reason' => 'kind' ) ); } $title = trim( sanitize_text_field( $title ) ); $title = '' !== $title ? mb_substr( $title, 0, 200 ) : self::default_title( $kind ); if ( in_array( $kind, Kinds::FREE, true ) ) { return self::free_unit( $kind, $title, $actor ); } $unit_id = (int) apply_filters( 'rooster_lms_course_builder_create_unit', 0, $kind, $title, 0, $actor->user_id ); if ( $unit_id <= 0 ) { DomainError::raise( ErrorCode::INVALID, array( 'reason' => 'kind_unavailable' ) ); } return array( $unit_id, (int) ( Services::unit_repo()->get_many( array( $unit_id ) )[ $unit_id ]->home_post_id ?? 0 ) ); } private static function free_unit( string $kind, string $title, Actor $actor ): array { list( $type, $lesson_kind ) = Kinds::target( $kind ); $post_id = (int) Switcher::with( Names::default_language(), static fn() => wp_insert_post( array( 'post_type' => UnitType::QUIZ === $type ? PostTypes::QUIZ : PostTypes::LESSON, 'post_title' => $title, 'post_content' => UnitType::H5P === $type ? \RoosterLMS\H5P\H5PProvider::BLOCK_MARKUP : '', 'post_status' => 'draft', 'post_author' => max( 0, $actor->user_id ), ), false, false ), false ); if ( $post_id <= 0 ) { DomainError::raise( ErrorCode::INVALID, array( 'reason' => 'create' ) ); } $unit_id = Services::courses()->ensure_unit_for_post( $post_id ); if ( $unit_id <= 0 ) { wp_delete_post( $post_id, true ); DomainError::raise( ErrorCode::INVALID, array( 'reason' => 'create' ) ); } if ( UnitType::ASSIGNMENT === $type || UnitType::LIVE === $type || UnitType::H5P === $type ) { Services::courses()->set_unit_type( $unit_id, $type, Actor::system() ); } elseif ( UnitType::LESSON === $type && LessonKind::TEXT !== $lesson_kind ) { Services::media()->update_lesson( $unit_id, array( 'kind' => $lesson_kind ), $actor ); } return array( $unit_id, $post_id ); } private static function addon_kinds( int $user_id ): array { $kinds = Kinds::picker( $user_id )['addon_kinds'] ?? array(); return array_values( array_filter( is_array( $kinds ) ? $kinds : array(), static fn( $k ): bool => is_string( $k ) && 1 === preg_match( '/^[a-z][a-z0-9_-]{0,31}$/', $k ) ) ); } public static function default_title( string $kind ): string { return match ( $kind ) { 'video' => __( 'New video lesson', 'rooster-lms' ), 'audio' => __( 'New audio lesson', 'rooster-lms' ), 'pdf' => __( 'New PDF lesson', 'rooster-lms' ), 'stream' => __( 'New stream', 'rooster-lms' ), 'live' => __( 'New live lesson', 'rooster-lms' ), 'quiz' => __( 'New quiz', 'rooster-lms' ), 'assignment' => __( 'New assignment', 'rooster-lms' ), 'scorm' => __( 'New SCORM package', 'rooster-lms' ), 'external' => __( 'New external tool', 'rooster-lms' ), default => __( 'New lesson', 'rooster-lms' ), }; } }