WordPress Integration Guide

Integrate SCORM functionality into your WordPress site using the AllureLMS SCORM API.

Table of Contents

Overview

This guide shows how to create a WordPress plugin that integrates with the SCORM API to:

  • Upload and manage SCORM packages
  • Display SCORM courses to learners
  • Track learner progress
  • Display completion certificates

Prerequisites

  • WordPress 5.0+
  • PHP 8.0+
  • SCORM API account and API key
  • Basic WordPress plugin development knowledge

Plugin Setup

1. Create Plugin Structure

wp-content/plugins/scorm-api-integration/
├── scorm-api-integration.php
├── includes/
│   ├── class-scorm-api-client.php
│   ├── class-scorm-admin.php
│   └── class-scorm-frontend.php
├── admin/
│   └── admin.php
└── public/
    └── public.php

2. Main Plugin File

<?php
/**
 * Plugin Name: SCORM API Integration
 * Description: Integrate AllureLMS SCORM API with WordPress
 * Version: 1.0.0
 */

if (!defined('ABSPATH')) {
    exit;
}

define('SCORM_API_VERSION', '1.0.0');
define('SCORM_API_PLUGIN_DIR', plugin_dir_path(__FILE__));

require_once SCORM_API_PLUGIN_DIR . 'includes/class-scorm-api-client.php';
require_once SCORM_API_PLUGIN_DIR . 'includes/class-scorm-admin.php';
require_once SCORM_API_PLUGIN_DIR . 'includes/class-scorm-frontend.php';

class SCORM_API_Integration {
    public function __construct() {
        add_action('plugins_loaded', array($this, 'init'));
    }

    public function init() {
        if (is_admin()) {
            new SCORM_Admin();
        } else {
            new SCORM_Frontend();
        }
    }
}

new SCORM_API_Integration();

3. API Client Class

<?php
// includes/class-scorm-api-client.php

class SCORM_API_Client {
    private $api_key;
    private $base_url;
    private $tenant_id;

    public function __construct() {
        $this->api_key = get_option('scorm_api_key');
        $this->base_url = get_option('scorm_api_url', 'https://app.allureconnect.com');
        $this->tenant_id = get_option('scorm_tenant_id');
    }

    public function upload_package($file_path, $uploaded_by) {
        $filename = basename($file_path);
        $file_size = filesize($file_path);

        $mint = wp_remote_post($this->base_url . '/api/v1/packages/upload-url', array(
            'headers' => array(
                'Authorization' => 'Bearer ' . $this->api_key,
                'Content-Type' => 'application/json',
            ),
            'body' => wp_json_encode(array(
                'tenant_id' => $this->tenant_id,
                'uploaded_by' => $uploaded_by,
                'filename' => $filename,
                'file_size' => $file_size,
            )),
            'timeout' => 120,
        ));
        if (is_wp_error($mint)) {
            return false;
        }
        $ticket = json_decode(wp_remote_retrieve_body($mint), true);
        if (empty($ticket['presigned_url'])) {
            return false;
        }

        $put = wp_remote_request($ticket['presigned_url'], array(
            'method' => 'PUT',
            'headers' => isset($ticket['required_put_headers']) ? $ticket['required_put_headers'] : array(),
            'body' => file_get_contents($file_path),
            'timeout' => 600,
        ));
        if (is_wp_error($put) || wp_remote_retrieve_response_code($put) >= 400) {
            return false;
        }

        $proc = wp_remote_post($this->base_url . '/api/v1/packages/process', array(
            'headers' => array(
                'Authorization' => 'Bearer ' . $this->api_key,
                'Content-Type' => 'application/json',
            ),
            'body' => wp_json_encode(array(
                'tenant_id' => $this->tenant_id,
                'uploaded_by' => $uploaded_by,
                'storage_path' => $ticket['storage_path'],
                'original_filename' => $filename,
            )),
            'timeout' => 300,
        ));
        if (is_wp_error($proc)) {
            return false;
        }

        return json_decode(wp_remote_retrieve_body($proc), true);
    }

    public function launch_session($package_id, $user_id) {
        $url = $this->base_url . '/api/v1/packages/' . $package_id . '/launch';
        
        $response = wp_remote_post($url, array(
            'headers' => array(
                'X-API-Key' => $this->api_key,
                'Content-Type' => 'application/json',
            ),
            'body' => json_encode(array(
                'user_id' => $user_id,
                'session_id' => wp_generate_uuid4(),
            )),
        ));

        if (is_wp_error($response)) {
            return false;
        }

        return json_decode(wp_remote_retrieve_body($response), true);
    }

    public function get_session($session_id) {
        $url = $this->base_url . '/api/v1/sessions/' . $session_id;
        
        $response = wp_remote_get($url, array(
            'headers' => array(
                'X-API-Key' => $this->api_key,
            ),
        ));

        if (is_wp_error($response)) {
            return false;
        }

        return json_decode(wp_remote_retrieve_body($response), true);
    }
}

Shortcode Implementation

SCORM Player Shortcode

<?php
// includes/class-scorm-frontend.php

class SCORM_Frontend {
    public function __construct() {
        add_shortcode('scorm_player', array($this, 'scorm_player_shortcode'));
        add_action('wp_enqueue_scripts', array($this, 'enqueue_scripts'));
    }

    public function scorm_player_shortcode($atts) {
        $atts = shortcode_atts(array(
            'package_id' => '',
            'width' => '100%',
            'height' => '800px',
        ), $atts);

        if (empty($atts['package_id'])) {
            return '<p>Package ID is required</p>';
        }

        $user_id = get_current_user_id();
        if (!$user_id) {
            return '<p>Please log in to view this course</p>';
        }

        $client = new SCORM_API_Client();
        $launch = $client->launch_session($atts['package_id'], $user_id);

        if (!$launch) {
            return '<p>Failed to launch course</p>';
        }

        // Store session ID in user meta
        update_user_meta($user_id, 'scorm_session_' . $atts['package_id'], $launch['session_id']);

        ob_start();
        ?>
        <div class="scorm-player-container">
            <iframe
                src="<?php echo esc_url($launch['launch_url']); ?>"
                width="<?php echo esc_attr($atts['width']); ?>"
                height="<?php echo esc_attr($atts['height']); ?>"
                frameborder="0"
                allow="fullscreen"
                title="SCORM Course"
            ></iframe>
        </div>
        <script>
            // Poll for completion
            setInterval(function() {
                fetch('<?php echo admin_url('admin-ajax.php'); ?>', {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/x-www-form-urlencoded',
                    },
                    body: 'action=check_scorm_progress&session_id=<?php echo $launch['session_id']; ?>'
                })
                .then(response => response.json())
                .then(data => {
                    if (data.completed) {
                        alert('Course completed!');
                    }
                });
            }, 10000);
        </script>
        <?php
        return ob_get_clean();
    }

    public function enqueue_scripts() {
        wp_enqueue_script('scorm-api', plugin_dir_url(__FILE__) . 'js/scorm-api.js', array('jquery'), '1.0.0', true);
    }
}

Usage

// In WordPress post/page editor:
[scorm_player package_id="pkg_abc123" width="100%" height="800px"]

Admin Interface

Settings Page

<?php
// includes/class-scorm-admin.php

class SCORM_Admin {
    public function __construct() {
        add_action('admin_menu', array($this, 'add_admin_menu'));
        add_action('admin_init', array($this, 'register_settings'));
    }

    public function add_admin_menu() {
        add_menu_page(
            'SCORM API',
            'SCORM API',
            'manage_options',
            'scorm-api',
            array($this, 'render_settings_page'),
            'dashicons-book-alt',
            30
        );
    }

    public function register_settings() {
        register_setting('scorm_api_settings', 'scorm_api_key');
        register_setting('scorm_api_settings', 'scorm_api_url');
        register_setting('scorm_api_settings', 'scorm_tenant_id');
    }

    public function render_settings_page() {
        ?>
        <div class="wrap">
            <h1>SCORM API Settings</h1>
            <form method="post" action="options.php">
                <?php settings_fields('scorm_api_settings'); ?>
                <table class="form-table">
                    <tr>
                        <th><label for="scorm_api_url">API URL</label></th>
                        <td>
                            <input type="url" id="scorm_api_url" name="scorm_api_url" 
                                   value="<?php echo esc_attr(get_option('scorm_api_url')); ?>" 
                                   class="regular-text" />
                        </td>
                    </tr>
                    <tr>
                        <th><label for="scorm_api_key">API Key</label></th>
                        <td>
                            <input type="password" id="scorm_api_key" name="scorm_api_key" 
                                   value="<?php echo esc_attr(get_option('scorm_api_key')); ?>" 
                                   class="regular-text" />
                        </td>
                    </tr>
                    <tr>
                        <th><label for="scorm_tenant_id">Tenant ID</label></th>
                        <td>
                            <input type="text" id="scorm_tenant_id" name="scorm_tenant_id" 
                                   value="<?php echo esc_attr(get_option('scorm_tenant_id')); ?>" 
                                   class="regular-text" />
                        </td>
                    </tr>
                </table>
                <?php submit_button(); ?>
            </form>
        </div>
        <?php
    }
}

Package Upload Interface

public function render_upload_page() {
    if (isset($_POST['upload_scorm'])) {
        $file = $_FILES['scorm_file'];
        $client = new SCORM_API_Client();
        $result = $client->upload_package($file['tmp_name'], get_current_user_id());
        
        if ($result) {
            // Store package ID in WordPress
            add_post_meta(get_the_ID(), 'scorm_package_id', $result['id']);
            echo '<div class="notice notice-success"><p>Package uploaded successfully!</p></div>';
        }
    }
    ?>
    <div class="wrap">
        <h1>Upload SCORM Package</h1>
        <form method="post" enctype="multipart/form-data">
            <input type="file" name="scorm_file" accept=".zip" required />
            <?php submit_button('Upload Package', 'primary', 'upload_scorm'); ?>
        </form>
    </div>
    <?php
}

User Progress Tracking

AJAX Handler

add_action('wp_ajax_check_scorm_progress', 'check_scorm_progress');
add_action('wp_ajax_nopriv_check_scorm_progress', 'check_scorm_progress');

function check_scorm_progress() {
    $session_id = sanitize_text_field($_POST['session_id']);
    $client = new SCORM_API_Client();
    $session = $client->get_session($session_id);

    if ($session) {
        // Update user meta
        update_user_meta(
            get_current_user_id(),
            'scorm_progress_' . $session_id,
            array(
                'completion_status' => $session['completion_status'],
                'score' => $session['score']['scaled'],
                'time_spent' => $session['time_spent_seconds'],
            )
        );

        wp_send_json_success(array(
            'completed' => $session['completion_status'] === 'completed',
            'score' => $session['score']['scaled'],
        ));
    }

    wp_send_json_error('Failed to get session');
}

Progress Display

function display_user_progress($user_id) {
    $packages = get_posts(array(
        'post_type' => 'scorm_course',
        'posts_per_page' => -1,
    ));

    foreach ($packages as $package) {
        $package_id = get_post_meta($package->ID, 'scorm_package_id', true);
        $session_id = get_user_meta($user_id, 'scorm_session_' . $package_id, true);
        
        if ($session_id) {
            $progress = get_user_meta($user_id, 'scorm_progress_' . $session_id, true);
            ?>
            <div class="scorm-progress-item">
                <h3><?php echo $package->post_title; ?></h3>
                <p>Status: <?php echo $progress['completion_status']; ?></p>
                <p>Score: <?php echo ($progress['score'] * 100); ?>%</p>
            </div>
            <?php
        }
    }
}

Complete Example

Full Plugin Structure

<?php
/**
 * Complete SCORM API WordPress Plugin
 */

// Main plugin file with all components integrated
// See individual class files above for implementation

Custom Post Type for Courses

function register_scorm_course_post_type() {
    register_post_type('scorm_course', array(
        'labels' => array(
            'name' => 'SCORM Courses',
            'singular_name' => 'SCORM Course',
        ),
        'public' => true,
        'has_archive' => true,
        'supports' => array('title', 'editor', 'thumbnail'),
    ));
}
add_action('init', 'register_scorm_course_post_type');

Best Practices

  1. Security: Always sanitize and validate user input
  2. Caching: Cache package metadata to reduce API calls
  3. Error Handling: Display user-friendly error messages
  4. Performance: Use AJAX for progress updates
  5. User Experience: Show loading states and progress indicators

Related Documentation


Last Updated: 2025-01-15