# Client Integration Contract

How a custom-skinned WordPress site (custom child-theme + site plugin) integrates
with MentorKit LMS Pro. This is the **stable contract** custom sites consume.
Treat it as semver-meaningful: breaking changes to anything documented here are
called out in `CHANGELOG.md` and gated by a MAJOR/MINOR bump.

> Reference implementation: the Avløserkortet site (`avloserkortet-site`
> monorepo) — Astra child-theme + `avlk-blocks` plugin, built entirely against
> this contract.

---

## 1. Division of responsibility

| Layer | Owner | Responsibility |
|---|---|---|
| Course **player** (`/courses/<slug>/view`) | **LMS Pro** | Renders `scorm-course-view.php`. Never fork or replace it. |
| Course **info / landing** page | **Client theme** | Custom `single-scorm_package.php` — branded marketing/landing. |
| Tracking, access, enrollment, completion | **LMS Pro** | Source of truth. Read via the public functions below. |
| Brand (colors, type, radius) | **Client theme** | A `tokens.css` that sets the `--scorm-*` contract (§3). |

Golden rule: **the theme reads state and re-skins; LMS Pro owns data and the player.**

---

## 2. Public PHP API

All functions are prefixed `scorm_player_*` and safe to call from a theme.
**Always guard with `function_exists()`** so the theme degrades gracefully if the
plugin is deactivated.

### Completion & progress
```php
scorm_player_user_has_completed( int $user_id, int $course_id ): bool
// true only when the user's tracking status is 'passed'.

scorm_player_get_user_course_progress( int $user_id, int $course_id ): array
// ['status' => 'passed'|'failed'|'attempted'|null, 'completed' => bool, 'score' => float|null]

scorm_player_get_user_group_tracking_status( int $user_id, int $group_id ): ?array
// group aggregate: total_courses, completed_courses, progress_percentage, status (or null)
```

### Access & availability
```php
scorm_player_get_course_access_state( int $course_id, int $user_id ): array
// ['has_access' => bool, 'access_type' => 'free'|'paid'|'open'|..., 'message' => string, ...]
// 0 for $user_id = anonymous viewer.

scorm_player_course_has_package_files( int $post_id ): bool
// true when the course has an uploaded, launchable SCORM package on disk.
```

### Routing (never hand-build URLs)
```php
scorm_player_get_course_view_url( int|WP_Post|string $course, array $query_args = [] ): string
// the learner "view"/player URL (Polylang-aware).

scorm_player_get_enroll_url( int|WP_Post|string $course ): string
// view URL + ?enroll_course=<id>. Use instead of hand-building the query arg.
```

### Example (info-page CTA + checklist)
```php
$uid = get_current_user_id();
if ( function_exists( 'scorm_player_user_has_completed' )
	&& scorm_player_user_has_completed( $uid, $course_id ) ) {
	$class = 'done';
}
$enroll = function_exists( 'scorm_player_get_enroll_url' )
	? scorm_player_get_enroll_url( $course )
	: '';
```

---

## 3. Brand theming — the `--scorm-*` token system

LMS Pro's admin **Theming** options (`scorm_primary_color`, `scorm_secondary_color`, …)
are the authoritative source of the `--scorm-*` brand tokens. `includes/admin/pages/theming-styles.php`
reads them and generates the full token set — including derived `*-rgb` triplets and
`color-mix()` surfaces — into an inline `<style id="scorm-player-custom-styles">` that
loads **late in `<head>`**, so it wins the cascade over any theme stylesheet.

> **Precedence (important):** the LMS Theming options win over `--scorm-*` declared in a
> theme `tokens.css`. Do **not** try to own `--scorm-*` from the theme — you would only
> override the top-level tokens while the derived ones stay computed from the LMS values.
> Set the brand in the Theming options instead.

### Where brand lives — one home each

| Layer | Home | Versioned as |
|---|---|---|
| `--scorm-*` (LMS surfaces: player, grids, dashboards, auth) | **LMS Theming options** (DB) | `scorm-theming.json` snapshot in the site/theme repo |
| `--color-*` (theme surfaces: landing, custom templates, blocks) | theme `tokens.css` (git) | git |

Both are driven from the **same brand hex values**: `/mentorkit-lms-setup` derives the
Theming options from the theme's design tokens (`wp option set`) and exports the
`scorm-theming.json` snapshot — so the LMS theming is git-versioned and reproducible,
not hand-clicked in admin. The theme `tokens.css` must **not** redefine `--scorm-*`.

### Theming options → tokens

Set explicitly with `scorm_use_astra_colors = 0` (don't derive from the Astra palette —
that couples LMS semantics to layout colors and limits you to the palette slots). Key options:

| Option | `--scorm-*` token | Drives |
|---|---|---|
| `scorm_primary_color` | `--scorm-primary-color` | CTAs, links, primary buttons, focus rings |
| `scorm_secondary_color` | `--scorm-secondary-color` | Secondary buttons / accents |
| `scorm_completed_color` | `--scorm-completed-color` | "Passed/done" state |
| `scorm_failed_color` | `--scorm-failed-color` | "Failed/error" state |
| `scorm_in_progress_color` | `--scorm-in-progress-color` | "Attempted/in progress" state |
| `scorm_heading_color` / `scorm_text_color` / `scorm_muted_text_color` | heading/text/muted | Typography ink |
| `scorm_surface_color` / `scorm_background_color` / `scorm_border_color` | surface/bg/border | Panels, page, rules |
| `scorm_button_text_color` / `scorm_button_radius` | button text / radius | Buttons |

(Full set: ~19 options in `theming-styles.php`; `*-rgb` triplets and `color-mix()` surfaces
are derived automatically from the above.)

The **course player** (`scorm-course-view.css`) honors these as of 1.3.0 — the player
chrome is on-brand, not WP-admin blue.

---

## 4. Template integration — the `course_view` deferral

A custom theme may override the **info** page but must **defer the player** to
LMS Pro. Mirror this `template_include` gate (priority 99):

```php
add_filter( 'template_include', function ( $template ) {
	if ( is_post_type_archive( 'scorm_package' ) || is_tax( 'scorm_category' ) ) {
		$custom = get_stylesheet_directory() . '/archive-scorm_package.php';
		if ( file_exists( $custom ) ) { return $custom; }
	}
	if ( is_singular( 'scorm_package' ) ) {
		// course_view=1 → the /view player route. Defer to LMS Pro.
		if ( (int) get_query_var( 'course_view' ) === 1 ) {
			return $template;
		}
		$custom = get_stylesheet_directory() . '/single-scorm_package.php';
		if ( file_exists( $custom ) ) { return $custom; }
	}
	return $template;
}, 99 );
```

LMS Pro sets the player template via the `single_template` filter; the gate above
overrides the **info** route only and returns `$template` unchanged when
`course_view=1`, so the player is never forked.

---

## 5. Composition hooks (prefer over template forks)

For the courses/groups grids and cards, compose via action hooks instead of
forking templates:

- Cards: `scorm_before/after_course_card`, `…_card_media`, `…_card_content`,
  `…_card_meta`, `…_card_actions` (and `…_group_card_*` equivalents).
- Grids: `scorm_before/after_courses_grid`, `…_grid_loop`, `…_grid_item`,
  `…_grid_pagination` (and `…_groups_grid_*`).
- Auth/profile: `scorm_after_my_profile_login_notice`.

---

## 6. Integration checklist (Definition of Done for a custom site)

- [ ] LMS Theming options (`scorm_*`) set from the theme's brand tokens via `/mentorkit-lms-setup` + committed as `scorm-theming.json` (§3); `scorm_use_astra_colors = 0`.
- [ ] Theme `tokens.css` owns `--color-*` only — does **not** redefine `--scorm-*`.
- [ ] State read via the §2 public functions, never raw post-meta — each behind `function_exists()`.
- [ ] Player deferred via the `course_view` gate (§4); `scorm-course-view.php` not forked.
- [ ] URLs via `get_course_view_url()` / `get_enroll_url()`, not hand-built query args.
- [ ] No brand CSS left in the Customizer DB — promoted to the versioned theme.

---

*Changes to this contract are versioned with the plugin. When adding/removing a
public function, hook, or token, update this file + `CHANGELOG.md` in the same PR.*
