.ef page templates. See Courses — Overview for setup and enrollment.
Backend functions
All functions are available in<script scope="backend"> blocks and in .ef files.
getCourses(opts?)
Returns all courses for the brand, each with is_assigned and started for the current customer.
sort supports:
enrollment_oldest_unfinished_first(default)title_asctitle_descpreferred(when used, passpreferredOrder: ['slug-or-title', ...])
getCategoriesWithCourses(opts?)
Same as getCourses but grouped by category. Returns an array of { category_slug, category_name, courses[] }.
getCoursesByCategorySlug(opts)
Returns courses in a single category. Useful for “Daily practices” or “Advanced” sections on a dashboard.
getCoursesForCustomer(opts?)
Returns only courses the current customer is enrolled in (same as getCourses({ assignedOnly: true })). Returns [] if no session or no enrollments.
getCourseForCustomer(opts)
Returns a single course with enrollment check — is_assigned and started are set for the current customer. Also returns the full modules[] and contents[] tree, plus progress_percent and completed_content_ids. Use this on the course detail page.
null if no course with that slug exists.
getCourseBySlug(slug)
Returns a single course without enrollment check. is_assigned and started will not be set. Use when you only need course metadata and don’t need to gate content.
getCourseLesson(opts)
Returns the resolved current lesson payload for lesson-player pages, including currentLesson, prevLessonUrl, nextLessonUrl, and progress stats.
getCompletedLessonsCount(opts?)
Returns the total number of completed lessons across all enrolled courses, or for a specific course when a slug is provided.
0 if the customer has no enrollments or no completed lessons.
getCourseProgress(opts?)
Returns a structured progress summary with completed/total lesson counts per course and an overall total. Useful for dashboards.
request.route_params
For dynamic slug pages (e.g. guided-course/{course}.ef), the slug is available via request.route_params.course.
Course list page
Show all courses, sorted so enrolled courses appear first. Useis_assigned and started to drive the CTA label.
With category tabs
Course detail page
Resolve the slug from the URL, check enrollment withgetCourseForCustomer, and gate content.
Auth wrapper for course pages
Use the auth wrapper pattern (@extends) so the login check and common data load happen once across all course pages:
Checking access to a specific product
If you want to gate course access by checking whether the customer bought a specific product (rather than relying on enrollment), you can cross-reference withgetOrders():
Prefer the enrollment-based check (
getCourseForCustomer) — it is more reliable and does not require iterating order history. Use the order-history fallback only if enrollment is not being set up via product linking.Lesson types and content
Each lesson inmod.contents has a type field:
Additional fields on each lesson:
Progress API
All progress routes are prefixed with/api/course/courses/:courseId/. Authentication is via the active course session (cookie). courseId, moduleId, and contentId are numeric IDs.
Mark a lesson complete
{ success: true, message, stats: { completed_lessons, total_lessons, completion_percent } }.
Save general progress
courseId, moduleId, and contentId go in the URL for the lesson-specific routes. The general saveProgress route keeps them in the body — useful when you want to upsert a progress record with a specific completed value (e.g. un-completing).
Save video position
Get all progress for a course
Get progress for a single lesson
Get course stats
course.completed_content_ids. Use this array to mark lessons visually on load, then update the UI optimistically when the API call succeeds.