Home / Admin / LifterLMS_DIR_AD_FPopulation – LifterLMS Course Sync – Detail Population Module
Duplicate Snippet

Embed Snippet on Your Site

LifterLMS_DIR_AD_FPopulation – LifterLMS Course Sync – Detail Population Module

* PURPOSE:
* - Hooks into main sync completion
* - Populates comprehensive course data (lessons, sections, quizzes, etc.)
* - Populates ACF fields on llms_content_sync CPT
* - Uses optimized queries to prevent performance issues
*
* WPCode Snippet - Run Everywhere - Priority 15

ismail daugherty PRO
<10
Code Preview
php
<?php
/**
 * LifterLMS Course Sync - Detail Population Module
 * 
 * PURPOSE:
 * - Hooks into main sync completion
 * - Populates comprehensive course data (lessons, sections, quizzes, etc.)
 * - Populates ACF fields on llms_content_sync CPT
 * - Uses optimized queries to prevent performance issues
 * 
 * WPCode Snippet - Run Everywhere - Priority 15
 */
defined( 'ABSPATH' ) || exit;
/**
 * Hook into sync completion to populate detailed data
 */
add_action( 'llms_content_sync_complete', 'llms_populate_course_details', 10, 3 );
/**
 * Main population function
 * 
 * @param int $course_id Original course ID
 * @param int $sync_post_id Sync post ID
 * @param bool $is_update Whether this is an update
 */
function llms_populate_course_details( $course_id, $sync_post_id, $is_update = false ) {
    
    // Verify LifterLMS is active
    if ( ! class_exists( 'LifterLMS' ) ) {
        error_log( 'LLMS_DETAIL: LifterLMS not active' );
        return;
    }
    
    // Get course object
    $course = llms_get_post( $course_id );
    if ( ! $course || ! is_a( $course, 'LLMS_Course' ) ) {
        error_log( 'LLMS_DETAIL: Invalid course ID ' . $course_id );
        return;
    }
    
    // Store source course ID for utility functions
    update_post_meta( $sync_post_id, '_source_course_id', $course_id );
    
    // Performance: Start timer
    $start_time = microtime( true );
    
    try {
        // Populate section details
        llms_populate_section_data( $course, $sync_post_id );
        
        // Populate lesson details
        llms_populate_lesson_data( $course, $sync_post_id );
        
        // Populate quiz details
        llms_populate_quiz_data( $course, $sync_post_id );
        
        // Populate assignment details
        llms_populate_assignment_data( $course, $sync_post_id );
        
        // Populate certificate details
        llms_populate_certificate_data( $course, $sync_post_id );
        
        // Populate custom ACF fields (if ACF is active)
        if ( function_exists( 'update_field' ) ) {
            llms_populate_acf_fields( $course, $sync_post_id );
        }
        
        // ✅ NEW: Populate the edit links
        if ( function_exists( 'llms_update_course_links' ) ) {
            llms_update_course_links( $sync_post_id, $course_id );
        }
        
        // Clean up transient
        delete_transient( 'llms_detail_populate_' . $sync_post_id );
        
        // Log completion time
        $execution_time = microtime( true ) - $start_time;
        error_log( sprintf( 'LLMS_DETAIL: Populated course %d in %.2f seconds', $course_id, $execution_time ) );
        
    } catch ( Exception $e ) {
        error_log( 'LLMS_DETAIL: Error populating course ' . $course_id . ': ' . $e->getMessage() );
    }
}
/**
 * Populate section details
 */
function llms_populate_section_data( $course, $sync_post_id ) {
    $sections = $course->get_sections( 'sections' );
    $section_details = [];
    
    foreach ( $sections as $section ) {
        $lesson_count = count( $section->get_lessons( 'ids' ) );
        
        $section_details[] = sprintf(
            "Section %d: %s\n- %d lessons\n- %s",
            $section->get( 'order' ),
            $section->get( 'title' ),
            $lesson_count,
            $section->get( 'content' ) ? 'Has description' : 'No description'
        );
    }
    
    $sections_list = implode( "\n\n", $section_details );
    update_post_meta( $sync_post_id, 'sections_details_list', $sections_list );
    
    // Log for debugging
    error_log( sprintf( 'LLMS_DETAIL: Processed %d sections for course %d', 
        count( $sections ), 
        $course->get( 'id' ) 
    ) );
}
/**
 * Populate lesson details
 */
function llms_populate_lesson_data( $course, $sync_post_id ) {
    $lessons = $course->get_lessons( 'lessons' );
    $lesson_details = [];
    $steps_details = [];
    
    foreach ( $lessons as $lesson ) {
        // Basic lesson info
        $lesson_info = sprintf(
            "Lesson: %s\n- Type: %s\n- Points: %d\n- Free: %s",
            $lesson->get( 'title' ),
            $lesson->get( 'video_embed' ) ? 'Video' : 'Standard',
            $lesson->get( 'points' ),
            $lesson->is_free() ? 'Yes' : 'No'
        );
        
        // Add quiz info if exists
        if ( $lesson->has_quiz() ) {
            $lesson_info .= "\n- Has Quiz: Yes";
        }
        
        // Add assignment info if exists
        $assignment_id = $lesson->get( 'assignment' );
        if ( $assignment_id ) {
            $lesson_info .= "\n- Has Assignment: Yes";
        }
        
        // Add prerequisite info
        if ( $lesson->has_prerequisite() ) {
            $prereq_id = $lesson->get( 'prerequisite' );
            $prereq_post = get_post( $prereq_id );
            if ( $prereq_post ) {
                $lesson_info .= "\n- Prerequisite: " . $prereq_post->post_title;
            }
        }
        
        // Add drip info
        $drip_method = $lesson->get( 'drip_method' );
        if ( $drip_method && $drip_method !== '' ) {
            $lesson_info .= "\n- Drip: " . ucfirst( $drip_method );
            $days = $lesson->get( 'days_before_available' );
            if ( $days ) {
                $lesson_info .= " (after {$days} days)";
            }
        }
        
        $lesson_details[] = $lesson_info;
        
        // Build step details (similar to form fields)
        $steps_details[] = sprintf(
            "Step %d: %s (%s)",
            count( $steps_details ) + 1,
            $lesson->get( 'title' ),
            $lesson->is_free() ? 'Free' : 'Locked'
        );
    }
    
    // Update meta
    $lessons_list = implode( "\n\n", $lesson_details );
    update_post_meta( $sync_post_id, 'lessons_details_list', $lessons_list );
    
    $steps_list = implode( "\n", $steps_details );
    update_post_meta( $sync_post_id, 'steps_details_list', $steps_list );
    
    error_log( sprintf( 'LLMS_DETAIL: Processed %d lessons', count( $lessons ) ) );
}
/**
 * Populate quiz details
 */
function llms_populate_quiz_data( $course, $sync_post_id ) {
    $lessons = $course->get_lessons( 'lessons' );
    $quiz_details = [];
    
    foreach ( $lessons as $lesson ) {
        if ( ! $lesson->has_quiz() ) {
            continue;
        }
        
        $quiz = $lesson->get_quiz();
        if ( ! $quiz ) {
            continue;
        }
        
        $questions = $quiz->get_questions();
        
        $quiz_info = sprintf(
            "Quiz: %s\n- Lesson: %s\n- Questions: %d\n- Pass Rate: %d%%\n- Time Limit: %s\n- Attempts: %s",
            $quiz->get( 'title' ),
            $lesson->get( 'title' ),
            count( $questions ),
            $quiz->get( 'passing_percent' ),
            $quiz->get( 'time_limit' ) ? $quiz->get( 'time_limit' ) . ' minutes' : 'None',
            $quiz->get( 'allowed_attempts' ) ?: 'Unlimited'
        );
        
        // Add question types breakdown
        $question_types = [];
        foreach ( $questions as $question ) {
            $type = get_post_meta( $question->get( 'id' ), '_llms_question_type', true );
            if ( ! isset( $question_types[ $type ] ) ) {
                $question_types[ $type ] = 0;
            }
            $question_types[ $type ]++;
        }
        
        if ( ! empty( $question_types ) ) {
            $quiz_info .= "\n- Question Types: ";
            $types = [];
            foreach ( $question_types as $type => $count ) {
                $types[] = ucfirst( $type ) . " ({$count})";
            }
            $quiz_info .= implode( ', ', $types );
        }
        
        $quiz_details[] = $quiz_info;
    }
    
    $quizzes_list = ! empty( $quiz_details ) 
        ? implode( "\n\n", $quiz_details )
        : 'No quizzes in this course';
    
    update_post_meta( $sync_post_id, 'quizzes_details_list', $quizzes_list );
    
    error_log( sprintf( 'LLMS_DETAIL: Found %d quizzes', count( $quiz_details ) ) );
}
/**
 * Populate assignment details
 */
function llms_populate_assignment_data( $course, $sync_post_id ) {
    $lessons = $course->get_lessons( 'lessons' );
    $assignment_details = [];
    
    foreach ( $lessons as $lesson ) {
        $assignment_id = $lesson->get( 'assignment' );
        
        if ( ! $assignment_id || get_post_type( $assignment_id ) !== 'llms_assignment' ) {
            continue;
        }
        
        $assignment = get_post( $assignment_id );
        if ( ! $assignment ) {
            continue;
        }
        
        $assignment_type = get_post_meta( $assignment_id, '_llms_assignment_type', true );
        $points_enabled = get_post_meta( $assignment_id, '_llms_points', true );
        $points = get_post_meta( $assignment_id, '_llms_points_value', true );
        $passing_grade = get_post_meta( $assignment_id, '_llms_assignment_passing_grade', true );
        
        $assignment_info = sprintf(
            "Assignment: %s\n- Lesson: %s\n- Type: %s\n- Points: %s\n- Pass Grade: %d%%",
            $assignment->post_title,
            $lesson->get( 'title' ),
            ucfirst( $assignment_type ?: 'upload' ),
            $points_enabled === 'yes' ? $points : 'Not graded',
            intval( $passing_grade )
        );
        
        // Add content preview if exists
        if ( ! empty( $assignment->post_content ) ) {
            $content_preview = wp_trim_words( strip_tags( $assignment->post_content ), 20 );
            $assignment_info .= "\n- Instructions: " . $content_preview;
        }
        
        $assignment_details[] = $assignment_info;
    }
    
    $assignments_list = ! empty( $assignment_details )
        ? implode( "\n\n", $assignment_details )
        : 'No assignments in this course';
    
    update_post_meta( $sync_post_id, 'assignments_details_list', $assignments_list );
    
    error_log( sprintf( 'LLMS_DETAIL: Found %d assignments', count( $assignment_details ) ) );
}
/**
 * Populate certificate details
 */
function llms_populate_certificate_data( $course, $sync_post_id ) {
    global $wpdb;
    
    // Get engagements for this course
    $engagements = $wpdb->get_results( $wpdb->prepare(
        "SELECT p.*, pm.meta_value as engagement_type, pm2.meta_value as engagement_id
         FROM {$wpdb->posts} p
         LEFT JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id AND pm.meta_key = '_llms_engagement_type'
         LEFT JOIN {$wpdb->postmeta} pm2 ON p.ID = pm2.post_id AND pm2.meta_key = '_llms_engagement'
         WHERE p.post_type = 'llms_engagement' 
         AND p.post_status = 'publish'
         AND p.ID IN (
            SELECT post_id FROM {$wpdb->postmeta} 
            WHERE meta_key = '_llms_engagement_trigger_post' 
            AND meta_value = %d
         )",
        $course->get( 'id' )
    ) );
    
    $certificate_details = [];
    $engagement_details = [];
    
    foreach ( $engagements as $engagement ) {
        $type = $engagement->engagement_type;
        $template_id = $engagement->engagement_id;
        
        if ( ! $template_id ) {
            continue;
        }
        
        $template_post = get_post( $template_id );
        if ( ! $template_post ) {
            continue;
        }
        
        // Get trigger details
        $trigger_type = get_post_meta( $engagement->ID, '_llms_trigger_type', true );
        $engagement_delay = get_post_meta( $engagement->ID, '_llms_engagement_delay', true );
        
        $trigger_desc = ucfirst( str_replace( '_', ' ', $trigger_type ) );
        if ( $engagement_delay > 0 ) {
            $trigger_desc .= " (+{$engagement_delay} days)";
        }
        
        switch ( $type ) {
            case 'certificate':
                $cert_info = sprintf(
                    "Certificate: %s\n- Trigger: %s\n- Template ID: %d",
                    $template_post->post_title,
                    $trigger_desc,
                    $template_id
                );
                
                // Check for merge codes
                if ( strpos( $template_post->post_content, '{{' ) !== false ) {
                    $cert_info .= "\n- Uses merge codes: Yes";
                }
                
                $certificate_details[] = $cert_info;
                break;
                
            case 'email':
                $email_info = sprintf(
                    "Email: %s\n- Trigger: %s",
                    $template_post->post_title,
                    $trigger_desc
                );
                $engagement_details[] = $email_info;
                break;
                
            case 'achievement':
                $achievement_info = sprintf(
                    "Achievement: %s\n- Trigger: %s",
                    $template_post->post_title,
                    $trigger_desc
                );
                $engagement_details[] = $achievement_info;
                break;
        }
    }
    
    // Update certificates
    $certificates_list = ! empty( $certificate_details )
        ? implode( "\n\n", $certificate_details )
        : 'No certificates for this course';
    
    update_post_meta( $sync_post_id, 'certificates_details_list', $certificates_list );
    
    // Update all engagements
    $all_engagements = array_merge( $certificate_details, $engagement_details );
    $engagements_list = ! empty( $all_engagements )
        ? implode( "\n\n", $all_engagements )
        : 'No engagements configured';
    
    update_post_meta( $sync_post_id, 'engagements_details_list', $engagements_list );
    
    error_log( sprintf( 'LLMS_DETAIL: Found %d certificates, %d total engagements', 
        count( $certificate_details ),
        count( $all_engagements )
    ) );
}
/**
 * Populate enrollment and restriction data
 */
function llms_populate_enrollment_data( $course, $sync_post_id ) {
    
    // Get course ID
    $course_id = $course->get( 'id' );
    $enrollment_details = [];
    
    // ✅ FIXED: Check if course is free using post meta instead of is_free()
    $is_free_meta = get_post_meta( $course_id, '_llms_is_free', true );
    if ( $is_free_meta === 'yes' ) {
        $enrollment_details[] = "Course Type: FREE - Open enrollment";
    }
    
    // ✅ FIXED: Get access plans using WP_Query instead of get_access_plans()
    $access_plans = new WP_Query( [
        'post_type'      => 'llms_access_plan',
        'posts_per_page' => -1,
        'meta_query'     => [
            [
                'key'     => '_llms_product_id',
                'value'   => $course_id,
                'compare' => '='
            ]
        ]
    ] );
    
    // Process access plans
    if ( $access_plans->have_posts() ) {
        foreach ( $access_plans->posts as $plan_post ) {
            $plan = new LLMS_Access_Plan( $plan_post->ID );
            
            $plan_info = sprintf(
                "Access Plan: %s\n- Price: %s\n- Period: %s\n- Trial: %s",
                $plan->get( 'title' ),
                $plan->is_free() ? 'Free' : '$' . $plan->get( 'price' ),
                $plan->get( 'access_period' ) ?: 'Lifetime',
                $plan->has_trial() ? 'Yes (' . $plan->get( 'trial_length' ) . ' days)' : 'No'
            );
            
            // Add payment schedule for paid plans
            if ( ! $plan->is_free() ) {
                $frequency = $plan->get( 'frequency' );
                if ( $frequency > 0 ) {
                    $plan_info .= sprintf( "\n- Payment: Every %d %s",
                        $frequency,
                        $plan->get( 'period' )
                    );
                }
            }
            
            // Add enrollment limits
            $enrollment_period = $plan->get( 'enrollment_period' );
            if ( $enrollment_period ) {
                $plan_info .= "\n- Enrollment: " . ucfirst( $enrollment_period );
            }
            
            $enrollment_details[] = $plan_info;
        }
    }
    
    // Get restriction settings
    $restriction_msg = get_post_meta( $course_id, '_llms_content_restricted_message', true );
    if ( $restriction_msg ) {
        $enrollment_details[] = "Restriction Message:\n" . wp_trim_words( $restriction_msg, 30 );
    }
    
    // Get prerequisites
    if ( $course->has_prerequisite( 'course' ) ) {
        // ✅ FIXED: Use get_prerequisite_id() instead of get_prerequisite()
        $prereq_id = $course->get_prerequisite_id( 'course' );
        $prereq_course = get_post( $prereq_id );
        if ( $prereq_course ) {
            $enrollment_details[] = "Prerequisite Course: " . $prereq_course->post_title;
        }
    }
    
    $enrollment_list = ! empty( $enrollment_details )
        ? implode( "\n\n", $enrollment_details )
        : 'Open enrollment - no restrictions';
    
    update_post_meta( $sync_post_id, 'enrollment_details_list', $enrollment_list );
    
    error_log( sprintf( 'LLMS_DETAIL: Found %d access plans', $access_plans->post_count ) );
}
/**
 * Populate custom ACF fields with additional computed data
 */
function llms_populate_acf_fields( $course, $sync_post_id ) {
    
    // Calculate total content duration (estimate)
    $lessons = $course->get_lessons( 'lessons' );
    $total_minutes = 0;
    
    foreach ( $lessons as $lesson ) {
        // Estimate based on content length
        $content = $lesson->get( 'content' );
        $word_count = str_word_count( strip_tags( $content ) );
        $read_time = ceil( $word_count / 200 ); // 200 words per minute average
        
        // Add video time if applicable
        $video_embed = $lesson->get( 'video_embed' );
        if ( $video_embed ) {
            $read_time += 10; // Default 10 minutes for video lessons
        }
        
        $total_minutes += $read_time;
    }
    
    $hours = floor( $total_minutes / 60 );
    $minutes = $total_minutes % 60;
    $duration_estimate = sprintf( '%d hours %d minutes', $hours, $minutes );
    
    update_field( 'estimated_duration', $duration_estimate, $sync_post_id );
    
    // Calculate complexity score
    $complexity_score = llms_calculate_complexity_score( $course );
    update_field( 'complexity_score', $complexity_score, $sync_post_id );
    
    // Get student enrollment count (if reporting add-on is active)
    if ( function_exists( 'llms_get_enrolled_students' ) ) {
        $enrolled_students = llms_get_enrolled_students( $course->get( 'id' ), 'enrolled' );
        $enrollment_count = is_array( $enrolled_students ) ? count( $enrolled_students ) : 0;
        update_field( 'current_enrollments', $enrollment_count, $sync_post_id );
    }
    
    // Add engagement metrics
    llms_populate_enrollment_data( $course, $sync_post_id );
    
    error_log( 'LLMS_DETAIL: ACF fields populated' );
}
/**
 * Calculate course complexity score
 */
function llms_calculate_complexity_score( $course ) {
    $score = 0;
    
    // Base score on number of sections
    $sections = $course->get_sections( 'sections' );
    $score += count( $sections ) * 2;
    
    // Add for lessons
    $lessons = $course->get_lessons( 'lessons' );
    $score += count( $lessons );
    
    // Add for assessments
    foreach ( $lessons as $lesson ) {
        if ( $lesson->has_quiz() ) {
            $score += 3;
        }
        if ( $lesson->get( 'assignment' ) ) {
            $score += 2;
        }
    }
    
    // Add for prerequisites
    if ( $course->has_prerequisite( 'course' ) ) {
        $score += 5;
    }
    
    // Add for drip content
    foreach ( $lessons as $lesson ) {
        if ( $lesson->get( 'drip_method' ) ) {
            $score += 1;
        }
    }
    
    // Normalize to 1-10 scale
    $normalized = min( 10, max( 1, round( $score / 10 ) ) );
    
    return $normalized;
}
/**
 * Emergency cleanup function
 */
add_action( 'admin_init', function() {
    if ( isset( $_GET['llms_detail_cleanup'] ) && current_user_can( 'manage_options' ) ) {
        
        // Clear all detail population transients
        global $wpdb;
        $wpdb->query( "DELETE FROM {$wpdb->options} WHERE option_name LIKE '_transient_llms_detail_populate_%'" );
        $wpdb->query( "DELETE FROM {$wpdb->options} WHERE option_name LIKE '_transient_timeout_llms_detail_populate_%'" );
        
        wp_die( 'Detail population transients cleared!' );
    }
} );

Comments

Add a Comment