Розробка LMS-платформи з підтримкою SCORM

Наша компанія займається розробкою, підтримкою та обслуговуванням сайтів будь-якої складності. Від простих односторінкових сайтів до масштабних кластерних систем, побудованих на мікро сервісах. Досвід розробників підтверджено сертифікатами від вендорів.

Розробка та обслуговування будь-яких видів сайтів:

Інформаційні сайти або веб-програми
Сайти візитки, landing page, корпоративні сайти, онлайн каталоги, квіз, промо-сайти, блоги, ресурси новин, інформаційні портали, форуми, агрегатори
Сайти або веб-програми електронної комерції
Інтернет-магазини, B2B-портали, маркетплейси, онлайн-обмінники, кешбек-сайти, біржі, дропшиппінг-платформи, парсери товарів
Веб-програми для управління бізнес-процесами
CRM-системи, ERP-системи, корпоративні портали, системи управління виробництвом, парсери інформації
Сайти або веб-програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, конструктори сайтів, портали надання електронних послуг, відеохостинги, тематичні портали

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Пропоновані послуги
Показано 1 з 1 послугУсі 2065 послуг
Розробка LMS-платформи з підтримкою SCORM
Складна
від 2 тижнів до 3 місяців
Часті питання

Наші компетенції:

Етапи розробки

Останні роботи

  • image_website-b2b-advance_0.png
    Розробка сайту компанії B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    874
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Розробка веб-сайту для компанії ФІКСПЕР
    851

Підтримка SCORM у LMS

SCORM (Sharable Content Object Reference Model) — стандарт упаковки e-learning контенту. Більшість корпоративних курсів, виконаних у Articulate Storyline, Adobe Captivate, iSpring — це SCORM-пакети. LMS повинна вміти їх завантажувати, запускати в iframe та отримувати дані про прогрес.

Що таке SCORM-пакет

SCORM-пакет — ZIP-архів з файлом imsmanifest.xml і HTML/JS/медіа-файлами курсу. Версії: SCORM 1.2 (найпоширеніша) та SCORM 2004 (4-та редакція).

Структура пакета:

course.zip
├── imsmanifest.xml        # метаданні, структура
├── index.html             # точка входу курсу
├── scorm_api.js           # реалізація SCORM API
└── content/
    ├── slide1.html
    ├── media/
    └── ...

SCORM API — міст між курсом і LMS

Курс комунікує з LMS через JavaScript API. LMS створює глобальний об'єкт API (SCORM 1.2) або API_1484_11 (SCORM 2004) у вікні, де запущено iframe:

// SCORM 1.2 API об'єкт — створюється на сторінці LMS
class ScormApi12 {
  private lessonStatus = 'not attempted';
  private suspendData = '';
  private score = 0;
  private sessionTime = '';
  private dataStore = new Map<string, string>();
  private onComplete: (data: ScormData) => void;

  constructor(onComplete: (data: ScormData) => void) {
    this.onComplete = onComplete;
  }

  LMSInitialize(_: string): string {
    this.lessonStatus = 'incomplete';
    return 'true';
  }

  LMSGetValue(element: string): string {
    switch (element) {
      case 'cmi.core.lesson_status': return this.lessonStatus;
      case 'cmi.suspend_data': return this.suspendData;
      case 'cmi.core.score.raw': return String(this.score);
      case 'cmi.core.lesson_location': return this.dataStore.get('lesson_location') ?? '';
      default: return this.dataStore.get(element) ?? '';
    }
  }

  LMSSetValue(element: string, value: string): string {
    switch (element) {
      case 'cmi.core.lesson_status':
        this.lessonStatus = value;
        break;
      case 'cmi.suspend_data':
        this.suspendData = value;
        break;
      case 'cmi.core.score.raw':
        this.score = Number(value);
        break;
      case 'cmi.core.session_time':
        this.sessionTime = value;
        break;
      default:
        this.dataStore.set(element, value);
    }
    return 'true';
  }

  LMSCommit(_: string): string {
    // Відправити дані на сервер (дроселювання — не частіше разу на 5 секунд)
    this.saveProgress();
    return 'true';
  }

  LMSFinish(_: string): string {
    this.onComplete({
      status: this.lessonStatus,
      score: this.score,
      suspendData: this.suspendData,
      sessionTime: this.sessionTime,
    });
    return 'true';
  }

  LMSGetLastError(): string { return '0'; }
  LMSGetErrorString(_: string): string { return 'No error'; }
  LMSGetDiagnostic(_: string): string { return ''; }

  private async saveProgress() {
    await fetch('/api/scorm/progress', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        status: this.lessonStatus,
        score: this.score,
        suspendData: this.suspendData,
      }),
    });
  }
}

Інjecção API у iframe

Курс шукає API у батьківських вікнах (window.parent.parent...). LMS встановлює об'єкт перед завантаженням iframe:

function ScormPlayer({ courseId, enrollmentId }) {
  const iframeRef = useRef<HTMLIFrameElement>(null);

  useEffect(() => {
    // Встановити SCORM API на поточне вікно — iframe знайде його через parent
    const api = new ScormApi12(async (data) => {
      await fetch(`/api/enrollments/${enrollmentId}/complete`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(data),
      });
    });

    // SCORM 1.2
    (window as any).API = api;
    // SCORM 2004
    (window as any).API_1484_11 = api;

    return () => {
      delete (window as any).API;
      delete (window as any).API_1484_11;
    };
  }, [enrollmentId]);

  return (
    <iframe
      ref={iframeRef}
      src={`/api/courses/${courseId}/launch`}
      className="w-full border-0"
      style={{ height: 'calc(100vh - 64px)' }}
      allow="camera; microphone; fullscreen"
      title="SCORM Course"
    />
  );
}

Завантаження та розпакування SCORM-пакета

import AdmZip from 'adm-zip';
import { parseStringPromise } from 'xml2js';

app.post('/api/courses/upload', authenticate, upload.single('scorm'), async (req, res) => {
  const zipBuffer = req.file!.buffer;
  const zip = new AdmZip(zipBuffer);

  // Розпакувати до сховища (S3 або локально)
  const courseId = crypto.randomUUID();
  const extractPath = `/courses/${courseId}`;

  zip.extractAllTo(path.join(process.env.STORAGE_PATH!, extractPath), true);

  // Розпарсити маніфест
  const manifestEntry = zip.getEntry('imsmanifest.xml');
  if (!manifestEntry) throw new Error('Invalid SCORM package: no imsmanifest.xml');

  const manifest = await parseStringPromise(manifestEntry.getData().toString());
  const title = manifest.manifest.organizations[0].organization[0].title[0];
  const launchUrl = manifest.manifest.resources[0].resource[0]['$']['href'];
  const scormVersion = manifest.manifest['$']['version']?.includes('1.2') ? '1.2' : '2004';

  const course = await db.courses.create({
    id: courseId,
    title,
    launchUrl: `${extractPath}/${launchUrl}`,
    scormVersion,
    uploadedBy: req.user.id,
  });

  res.json(course);
});

Зберігання прогресу

CREATE TABLE scorm_progress (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  enrollment_id UUID REFERENCES enrollments(id),
  lesson_status VARCHAR(50),  -- 'passed' | 'failed' | 'completed' | 'incomplete'
  score NUMERIC(5,2),
  suspend_data TEXT,          -- для закладок та стану курсу
  session_time INTERVAL,
  completed_at TIMESTAMPTZ,
  updated_at TIMESTAMPTZ DEFAULT now()
);

SCORM 1.2 vs SCORM 2004

Параметр SCORM 1.2 SCORM 2004
API об'єкт window.API window.API_1484_11
Статусів passed/failed/completed/incomplete passed/failed/completed/incomplete/not attempted/unknown
Оцінка 0–100 0.0–1.0 (min/max/raw)
Прогрес suspend_data suspend_data + adl.nav
Поширеність Широка Менше

Строки виконання

Підтримка SCORM 1.2 з завантаженням пакетів, API-об'єктом та зберіганням прогресу — 1–1.5 тижні. З підтримкою SCORM 2004 — ще 3–5 днів.