Home / Admin / DIR LIFTER SYNC 3
Duplicate Snippet

Embed Snippet on Your Site

DIR LIFTER SYNC 3

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
 * 
 * HOOKS INTO:
 * - llms_content_sync_complete (from Snippet 1)
 * 
 * DEPENDENCIES:
 * - Main LifterLMS Sync Engine (Snippet 1)
 * - LifterLMS plugin
 * - ACF Pro (optional, for custom fields)
 */
// ============================================
// MAIN DETAIL POPULATION HOOK
// ============================================
add_action('llms_content_sync_complete', 'llms_populate_course_details', 10, 3);
function llms_populate_course_details($course_id, $sync_post_id, $is_update) {
    
    // Verify LifterLMS is active
    if (!class_exists('LLMS_Course')) {
        return;
    }
    
    // Rate limiting: Skip if recently populated (prevents duplicate operations)
    $detail_transient = 'llms_detail_lock_' . $sync_post_id;
    if (get_transient($detail_transient)) {
        return;
    }
    set_transient($detail_transient, 1, 120); // 2-minute lock
    
    try {
        // Get course object
        $course = new LLMS_Course($course_id);
        
        if (!$course || !$course->get('id')) {
            return;
        }
        
        // Populate comprehensive course structure
        llms_populate_course_structure($course, $sync_post_id);
        
        // Populate enrollment data
        llms_populate_enrollment_data($course, $sync_post_id);
        
        // Populate certificates and achievements
        llms_populate_certificates_achievements($course_id, $sync_post_id);
        
        // Populate custom ACF fields (if ACF is active)
        if (function_exists('update_field')) {
            llms_populate_acf_fields($course, $sync_post_id);
        }
        
        // Clean up transient
        delete_transient($detail_transient);
        
    } catch (Exception $e) {
        error_log('LifterLMS Detail Population Error: ' . $e->getMessage());
        delete_transient($detail_transient);
    }
}
// ============================================
// COURSE STRUCTURE (LESSONS, SECTIONS, QUIZZES)
// ============================================
function llms_populate_course_structure($course, $sync_post_id) {
    
    $course_structure = array();
    $all_lessons_data = array();
    $all_quizzes = array();
    
    // Get sections with lessons (optimized query)
    $sections = $course->get_sections('sections');
    
    if (empty($sections)) {
        update_post_meta($sync_post_id, '_course_structure', array());
        return;
    }
    
    foreach ($sections as $section) {
        $section_data = array(
            'id'    => $section->get('id'),
            'title' => $section->get('title'),
            'order' => $section->get('order'),
            'lessons' => array()
        );
        
        // Get lessons for this section
        $lessons = $section->get_lessons('lessons');
        
        foreach ($lessons as $lesson) {
            $lesson_data = array(
                'id'              => $lesson->get('id'),
                'title'           => $lesson->get('title'),
                'excerpt'         => $lesson->get('excerpt'),
                'order'           => $lesson->get('order'),
                'permalink'       => get_permalink($lesson->get('id')),
                'parent_section'  => $lesson->get('parent_section'),
                'parent_course'   => $lesson->get('parent_course'),
                'has_quiz'        => $lesson->has_quiz(),
                'quiz_enabled'    => $lesson->is_quiz_enabled(),
                'points'          => $lesson->get('points'),
                'has_prerequisite' => $lesson->has_prerequisite(),
                'prerequisite'    => $lesson->get('prerequisite'),
                'drip_method'     => $lesson->get('drip_method'),
                'drip_days'       => $lesson->get('days_before_available'),
                'require_passing_grade' => $lesson->get('require_passing_grade'),
                'video_embed'     => $lesson->get('video_embed'),
                'audio_embed'     => $lesson->get('audio_embed'),
                'featured_image'  => get_the_post_thumbnail_url($lesson->get('id'), 'medium')
            );
            
            // Get quiz data if exists
            if ($lesson->has_quiz()) {
                $quiz_id = $lesson->get('quiz');
                $quiz_data = llms_extract_quiz_data($quiz_id);
                $lesson_data['quiz'] = $quiz_data;
                $all_quizzes[] = $quiz_data;
            }
            
            // Check for assignments (if assignments add-on active)
            if ($lesson->get('assignment_enabled') === 'yes') {
                $lesson_data['has_assignment'] = true;
                $lesson_data['assignment_id'] = $lesson->get('assignment');
            }
            
            $section_data['lessons'][] = $lesson_data;
            $all_lessons_data[] = $lesson_data;
            
            // Free memory
            unset($lesson, $lesson_data);
        }
        
        $course_structure[] = $section_data;
        
        // Free memory
        unset($section, $section_data, $lessons);
    }
    
    // Store complete course structure
    update_post_meta($sync_post_id, '_course_structure', $course_structure);
    update_post_meta($sync_post_id, '_all_lessons', $all_lessons_data);
    update_post_meta($sync_post_id, '_all_quizzes', $all_quizzes);
    update_post_meta($sync_post_id, '_quiz_count', count($all_quizzes));
    
    // Free memory
    unset($course_structure, $all_lessons_data, $all_quizzes);
}
// ============================================
// QUIZ DATA EXTRACTION
// ============================================
function llms_extract_quiz_data($quiz_id) {
    
    if (!class_exists('LLMS_Quiz')) {
        return array();
    }
    
    $quiz = new LLMS_Quiz($quiz_id);
    
    if (!$quiz || !$quiz->get('id')) {
        return array();
    }
    
    $quiz_data = array(
        'id'               => $quiz->get('id'),
        'title'            => $quiz->get('title'),
        'content'          => $quiz->get('content'),
        'lesson_id'        => $quiz->get('lesson_id'),
        'passing_percent'  => $quiz->get('passing_percent'),
        'time_limit'       => $quiz->get('time_limit'),
        'limit_time'       => $quiz->has_time_limit(),
        'limit_attempts'   => $quiz->has_attempt_limit(),
        'allowed_attempts' => $quiz->get('allowed_attempts'),
        'show_correct_answer' => $quiz->get('show_correct_answer'),
        'random_answers'   => $quiz->get('random_answers'),
        'show_results'     => $quiz->get('show_results'),
        'questions'        => array()
    );
    
    // Get questions (limit to prevent memory issues)
    $questions = $quiz->get_questions();
    
    if (!empty($questions)) {
        $question_count = 0;
        foreach ($questions as $question) {
            // Limit to 50 questions to prevent memory exhaustion
            if ($question_count >= 50) {
                break;
            }
            
            $question_data = array(
                'id'          => $question->get('id'),
                'title'       => $question->get('title'),
                'description' => $question->get('description'),
                'type'        => $question->get('question_type'),
                'points'      => $question->get('points'),
                'choices_count' => count($question->get_choices())
            );
            
            $quiz_data['questions'][] = $question_data;
            $question_count++;
            
            unset($question, $question_data);
        }
    }
    
    $quiz_data['question_count'] = count($quiz_data['questions']);
    
    return $quiz_data;
}
// ============================================
// ENROLLMENT DATA
// ============================================
function llms_populate_enrollment_data($course, $sync_post_id) {
    
    $course_id = $course->get('id');
    
    // Get student enrollment count (cached query)
    $transient_key = 'llms_enrollment_count_' . $course_id;
    $enrollment_count = get_transient($transient_key);
    
    if (false === $enrollment_count) {
        // Query enrollments
        global $wpdb;
        $enrollment_count = $wpdb->get_var($wpdb->prepare("
            SELECT COUNT(DISTINCT user_id)
            FROM {$wpdb->prefix}lifterlms_user_postmeta
            WHERE post_id = %d
            AND meta_key = '_status'
            AND meta_value = 'enrolled'
        ", $course_id));
        
        // Cache for 1 hour
        set_transient($transient_key, $enrollment_count, HOUR_IN_SECONDS);
    }
    
    update_post_meta($sync_post_id, '_enrollment_count', $enrollment_count);
    
    // Get completion statistics (cached)
    $completion_transient = 'llms_completion_count_' . $course_id;
    $completion_count = get_transient($completion_transient);
    
    if (false === $completion_count) {
        global $wpdb;
        $completion_count = $wpdb->get_var($wpdb->prepare("
            SELECT COUNT(DISTINCT user_id)
            FROM {$wpdb->prefix}lifterlms_user_postmeta
            WHERE post_id = %d
            AND meta_key = '_is_complete'
            AND meta_value = 'yes'
        ", $course_id));
        
        set_transient($completion_transient, $completion_count, HOUR_IN_SECONDS);
    }
    
    update_post_meta($sync_post_id, '_completion_count', $completion_count);
    
    // Calculate completion rate
    if ($enrollment_count > 0) {
        $completion_rate = round(($completion_count / $enrollment_count) * 100, 2);
        update_post_meta($sync_post_id, '_completion_rate', $completion_rate);
    }
    
    // Access restrictions
    $is_free = ($course->is_free()) ? 'yes' : 'no';
    update_post_meta($sync_post_id, '_is_free_course', $is_free);
    
    // Get access plans (lightweight)
    $access_plans = $course->get_access_plans();
    $plan_count = is_array($access_plans) ? count($access_plans) : 0;
    update_post_meta($sync_post_id, '_access_plan_count', $plan_count);
}
// ============================================
// CERTIFICATES AND ACHIEVEMENTS
// ============================================
function llms_populate_certificates_achievements($course_id, $sync_post_id) {
    
    // Get certificates awarded for this course
    $certificates = get_posts(array(
        'post_type'      => 'llms_certificate',
        'posts_per_page' => -1,
        'meta_query'     => array(
            array(
                'key'   => '_llms_engagement_trigger_post',
                'value' => $course_id
            )
        ),
        'fields' => 'ids'
    ));
    
    update_post_meta($sync_post_id, '_certificate_templates', $certificates);
    update_post_meta($sync_post_id, '_has_certificate', !empty($certificates) ? 'yes' : 'no');
    
    // Get achievements
    $achievements = get_posts(array(
        'post_type'      => 'llms_achievement',
        'posts_per_page' => -1,
        'meta_query'     => array(
            array(
                'key'   => '_llms_engagement_trigger_post',
                'value' => $course_id
            )
        ),
        'fields' => 'ids'
    ));
    
    update_post_meta($sync_post_id, '_achievement_templates', $achievements);
    update_post_meta($sync_post_id, '_has_achievement', !empty($achievements) ? 'yes' : 'no');
    
    // Get engagements (emails, etc.)
    $engagements = get_posts(array(
        'post_type'      => 'llms_engagement',
        'posts_per_page' => -1,
        'meta_query'     => array(
            array(
                'key'   => '_llms_engagement_trigger_post',
                'value' => $course_id
            )
        )
    ));
    
    $engagement_data = array();
    foreach ($engagements as $engagement) {
        $engagement_data[] = array(
            'id'      => $engagement->ID,
            'title'   => $engagement->post_title,
            'trigger' => get_post_meta($engagement->ID, '_llms_engagement_trigger_type', true),
            'type'    => get_post_meta($engagement->ID, '_llms_engagement_type', true)
        );
        unset($engagement);
    }
    
    update_post_meta($sync_post_id, '_engagements', $engagement_data);
}
// ============================================
// ACF FIELD POPULATION
// ============================================
function llms_populate_acf_fields($course, $sync_post_id) {
    
    // Only run if ACF is active
    if (!function_exists('update_field')) {
        return;
    }
    
    $course_id = $course->get('id');
    
    // Example ACF field mappings
    // Customize these field names to match your ACF field setup
    
    // Text fields
    update_field('course_difficulty', $course->get('difficulty'), $sync_post_id);
    update_field('course_length', $course->get('length'), $sync_post_id);
    
    // Number fields
    update_field('lesson_count', get_post_meta($sync_post_id, '_lesson_count', true), $sync_post_id);
    update_field('section_count', get_post_meta($sync_post_id, '_section_count', true), $sync_post_id);
    update_field('quiz_count', get_post_meta($sync_post_id, '_quiz_count', true), $sync_post_id);
    
    // Enrollment data
    update_field('enrollment_count', get_post_meta($sync_post_id, '_enrollment_count', true), $sync_post_id);
    update_field('completion_count', get_post_meta($sync_post_id, '_completion_count', true), $sync_post_id);
    update_field('completion_rate', get_post_meta($sync_post_id, '_completion_rate', true), $sync_post_id);
    
    // True/False fields
    update_field('is_free_course', $course->is_free(), $sync_post_id);
    update_field('has_certificate', get_post_meta($sync_post_id, '_has_certificate', true) === 'yes', $sync_post_id);
    update_field('has_achievement', get_post_meta($sync_post_id, '_has_achievement', true) === 'yes', $sync_post_id);
    update_field('has_prerequisite', $course->has_prerequisite('course'), $sync_post_id);
    
    // Relationship field (prerequisite course)
    if ($course->has_prerequisite('course')) {
        $prereq_id = $course->get('prerequisite');
        update_field('prerequisite_course', $prereq_id, $sync_post_id);
    }
    
    // URL fields
    update_field('course_url', get_permalink($course_id), $sync_post_id);
    
    // WYSIWYG fields
    update_field('course_content', $course->get('content'), $sync_post_id);
    
    // Image field (featured image)
    $thumbnail_id = get_post_thumbnail_id($course_id);
    if ($thumbnail_id) {
        update_field('course_featured_image', $thumbnail_id, $sync_post_id);
    }
    
    // Repeater field example: Course Structure
    $course_structure = get_post_meta($sync_post_id, '_course_structure', true);
    if (!empty($course_structure) && is_array($course_structure)) {
        
        $repeater_data = array();
        
        foreach ($course_structure as $section) {
            $section_row = array(
                'section_title' => $section['title'],
                'section_order' => $section['order'],
                'lesson_count'  => count($section['lessons'])
            );
            $repeater_data[] = $section_row;
        }
        
        // Only update if not too large (prevent ACF performance issues)
        if (count($repeater_data) <= 50) {
            update_field('course_sections', $repeater_data, $sync_post_id);
        }
    }
    
    // Flexible content or serialized data field
    $all_lessons = get_post_meta($sync_post_id, '_all_lessons', true);
    if (!empty($all_lessons)) {
        update_field('course_lessons_data', $all_lessons, $sync_post_id);
    }
    
    // Taxonomy fields (if ACF Taxonomy fields used)
    $categories = wp_get_post_terms($course_id, 'course_cat', array('fields' => 'ids'));
    if (!empty($categories)) {
        update_field('course_categories', $categories, $sync_post_id);
    }
    
    $tags = wp_get_post_terms($course_id, 'course_tag', array('fields' => 'ids'));
    if (!empty($tags)) {
        update_field('course_tags', $tags, $sync_post_id);
    }
    
    // Date field (last sync time)
    update_field('last_sync_date', current_time('Y-m-d H:i:s'), $sync_post_id);
}
// ============================================
// CACHE INVALIDATION ON COMPLETION
// ============================================
add_action('lifterlms_course_completed', 'llms_invalidate_sync_cache', 10, 2);
add_action('llms_user_enrolled_in_course', 'llms_invalidate_sync_cache', 10, 2);
add_action('llms_user_removed_from_course', 'llms_invalidate_sync_cache', 10, 2);
function llms_invalidate_sync_cache($user_id, $course_id) {
    // Clear enrollment count cache when enrollment status changes
    delete_transient('llms_enrollment_count_' . $course_id);
    delete_transient('llms_completion_count_' . $course_id);
    
    // Optionally trigger a lightweight re-sync
    // Only update enrollment numbers, not full structure
    $synced = get_posts(array(
        'post_type' => 'llms_content_sync',
        'posts_per_page' => 1,
        'meta_query' => array(
            array(
                'key' => '_source_course_id',
                'value' => $course_id
            )
        ),
        'fields' => 'ids'
    ));
    
    if (!empty($synced)) {
        $sync_post_id = $synced[0];
        
        // Quick enrollment update (doesn't trigger full sync)
        $course = new LLMS_Course($course_id);
        llms_populate_enrollment_data($course, $sync_post_id);
    }
}
// ============================================
// UTILITY FUNCTIONS
// ============================================
/**
 * Get synced post ID for a course
 */
function llms_get_synced_post_id($course_id) {
    $synced = get_posts(array(
        'post_type' => 'llms_content_sync',
        'posts_per_page' => 1,
        'meta_query' => array(
            array(
                'key' => '_source_course_id',
                'value' => $course_id
            )
        ),
        'fields' => 'ids'
    ));
    
    return !empty($synced) ? $synced[0] : 0;
}
/**
 * Clear all detail population locks (emergency use)
 */
function llms_clear_detail_locks() {
    global $wpdb;
    $wpdb->query("DELETE FROM {$wpdb->options} WHERE option_name LIKE '_transient_llms_detail_lock_%'");
    $wpdb->query("DELETE FROM {$wpdb->options} WHERE option_name LIKE '_transient_timeout_llms_detail_lock_%'");
}

Comments

Add a Comment