Skip to main content

Adhrit LMS — Learning Management Suite Technical Doc

Uncategorised
78 min read Hits: 4

Detailed Project Documentation · Views, Fields & Full Issue Log

Adhrit LMS — Learning Management Suite

Every admin and site view described as a User Story with its full field list, mapped to file-level tasks and progress — plus a complete Issues & Fixes Log of every requirement and bug ever opened or resolved across the suite (component, three modules, plugin, REST API).

Suite: Adhrit LMS 4.7.0Component: com_adhritlms 4.7.0Overall: 99%Issues logged: 102Author: Mix Agile
DONE Shipped & verified
ACTIVE Ongoing (QA / live verification)
IN PROGRESS Planned / not yet shipped
How to read this. Each Epic gives context, then its User Stories — one per view/screen (admin, frontend, settings, API or module). Each story describes the view and lists all its fields (name · type · purpose), its columns/controls, and a task table (technical work + progress). The final Issues & Fixes Log lists every requirement and bug ever raised, with the version it landed in and its status. Paths are relative to com_adhritlms/ (admin / site / api apps) or the module/plugin root.

Delivery Summary

Epic-level status across the whole suite. Completion is task-weighted (shipped = 100%, live-verification = 92%, planned = 45%). 11 of 15 epics fully shipped; overall 99%.
Epic Scope Status Completion
E1 · Platform, Architecture & Packaging MVC bootstrap, SEF router, 18-table schema + idempotent migration, one-step package DONE
 
100%
E2 · Admin Dashboard & Navigation Cpanel dashboard (KPIs, charts), grouped collapsible sidebar, mobile drawer DONE
 
100%
E3 · Course Content Management Course → Chapter → Lesson + Categories: list/edit views, cascades, CSV import DONE
 
100%
E4 · Course Editor Single-screen Course Editor: in-place forms, clone/reuse, drag reorder, content map DONE
 
100%
E5 · Assessments & Quiz Gating Quiz authoring + results + server-side Lesson→Chapter→Course gating engine DONE
 
100%
E6 · People & Events Teacher & speaker profiles, events with mapped venues + online links DONE
 
100%
E7 · Commerce & Payments Orders, invoices, coupons, reviews + working PayPal (IPN) & UPI (deep-link/QR) checkout ACTIVE
 
98%
E8 · Certificates & Verification Customizable certificates (4 templates, borders/frames) + public verification DONE
 
100%
E9 · Settings & Configuration Consolidated settings (General/Payment/Certificates/Appearance/Editor/Invoices/ACL) DONE
 
100%
E10 · Backup, Restore & Bulk Import Full JSON backup/restore (2 modes) + per-course JSON/XML + CSV importers DONE
 
100%
E11 · Frontend Learner Experience Catalog, course page, gated lesson player, checkout, tabbed My Learning hub IN PROGRESS
 
95%
E12 · REST API & Mobile Catalog JSON:API (read-only) + per-user Learner API (login/token, me/*, learn, orders) ACTIVE
 
99%
E13 · Companion Site Modules Three template-agnostic modules: Catalog, Recommended, Carousel IN PROGRESS
 
95%
E14 · Security Scan Report Full security audit snapshot: 0 Critical/High/Medium, 5 Low fixed (4.2.17); all areas verified secure DONE
 
100%
E15 · Teacher Portal & Media Governance Frontend Teacher Portal + Teacher REST API: own-items authoring, governed uploads, JSON import/export DONE
 
100%
Whole suite Component + 3 modules + Web Services plugin + REST API ACTIVE
 
99%

Delivery by Component

The same report broken down per deliverable — the component and its three site modules and the Web Services plugin — each with the user stories it owns and their status.

com_adhritlms (component)

com_adhritlms 4.7.0 — admin · site · api apps
IN PROGRESS
 
99%

The full LMS: platform/packaging, admin dashboard & navigation, course content, course editor, assessments & gating, people/events, commerce, certificates, settings, backup and the whole frontend learner experience.

Story View / Area Status
US 1.1 Component bootstrap & routing DONE
US 1.2 Install, schema & migration DONE
US 1.3 One-step package DONE
US 2.1 Dashboard — Cpanel view DONE
US 2.2 Component sidebar & menu visibility DONE
US 2.3 Mobile navigation drawer DONE
US 3.1 Courses — list view DONE
US 3.2 Course — edit view DONE
US 3.3 Course Categories — list & edit DONE
US 3.4 Chapters — list & edit DONE
US 3.5 Lessons — list & edit DONE
US 4.1 Course Editor — builder DONE
US 4.2 Content reuse, reorder & map DONE
US 5.1 Quiz Questions — list & edit DONE
US 5.2 Quiz Results — list & edit DONE
US 5.3 Quiz taking & server-side gating DONE
US 6.1 Teachers — list & edit DONE
US 6.2 Speakers — list & edit DONE
US 6.3 Events & Event Categories DONE
US 7.1 Orders — list & edit DONE
US 7.2 Invoices — list & printable invoice DONE
US 7.3 Coupons & Reviews DONE
US 7.4 Checkout & working payments ACTIVE
US 8.1 Certificate design (Settings) & admin records DONE
US 8.2 Certificate render, download, share & verify DONE
US 9.1 General (with Security / Profile / Maps sections) DONE
US 9.2 Payment settings DONE
US 9.3 Certificates settings DONE
US 9.4 Appearance settings DONE
US 9.5 Course Editor settings DONE
US 9.6 Invoice settings DONE
US 9.7 Permissions DONE
US 10.1 Backup & Restore — view DONE
US 10.2 Per-course export/import & CSV imports DONE
US 11.1 Courses catalog & category views DONE
US 11.2 Course page (enroll) DONE
US 11.3 Lesson player DONE
US 11.4 Cart / Checkout / Purchases DONE
US 11.5 My Learning — profile hub DONE
US 11.6 Invoice — printable view IN PROGRESS
US 14.1 SQL Injection DONE
US 14.2 Cross-Site Scripting (XSS) & output encoding DONE
US 14.3 Cross-Site Request Forgery (CSRF) DONE
US 14.4 Broken Access Control / IDOR DONE
US 14.5 File Upload & Path Traversal DONE
US 14.6 Authentication & Session Tokens DONE
US 14.7 SSRF & Open Redirect DONE
US 14.8 Unsafe PHP / Injection Sinks DONE
US 14.9 Low-severity findings fixed in 4.2.17 DONE
US 15.1 Teacher user group & access DONE
US 15.2 Frontend course editor DONE
US 15.3 Media governance DONE
US 15.4 Own-items only & deletion policy DONE
US 15.5 Teacher REST API (mobile) DONE
US 15.6 JSON course import / export DONE
US 15.7 Drag-and-drop content editor DONE
US 15.8 Teacher profile media, email & website DONE
US 15.9 Teacher API switch DONE

mod_adhritlmscatalog (Catalog module)

mod_adhritlmscatalog 1.0.1
DONE
 
100%

One configurable site module that lists any LMS content type (courses, categories, teachers, events, event categories) or a course-search box, with a 1×1–4×4 grid and a CTA button.

Story View / Area Status
US 13.1 Catalog module DONE

mod_adhritlmsrecommended (Recommended module)

mod_adhritlmsrecommended 1.0.0
DONE
 
100%

Personalised course recommendations — learner preferences → enrolled categories; guests → admin picks; hide-enrolled and latest top-up. Never cached.

Story View / Area Status
US 13.2 Recommended Courses module DONE

mod_adhritlmscarousel (Carousel module)

mod_adhritlmscarousel 1.5.1
IN PROGRESS
 
94%

A full carousel/slider: Banner/Slim/Small modes, 20 presets, 20 animations, 20 Bootstrap-Icon arrows, 11 clickable bullet types with orientation, per-slide colour/icon/thumbnail, spacing controls, typography and CTA. Robust image handling — a missing/renamed image degrades to the placeholder and never blanks the carousel.

Story View / Area Status
US 13.3 Carousel module — Content DONE
US 13.4 Carousel module — Layout & Style DONE
US 13.5 Carousel module — Carousel controls IN PROGRESS
US 13.6 Carousel module — Typography, Button & Appearance DONE

plg_webservices_adhritlms (Web Services plugin)

plg_webservices_adhritlms 1.5.0
ACTIVE
 
99%

Registers all Adhrit LMS REST routes with the Joomla API application — the read-only catalog JSON:API and the authenticated per-user Learner API (login/token, me/*, learning actions, orders).

Story View / Area Status
US 12.1 Catalog JSON:API DONE
US 12.2 Learner API (login + me/*) ACTIVE
US 12.3 Teacher API (mobile course authoring) DONE

Web Services (REST API) — Endpoints & Postman Testing

The plg_webservices_adhritlms plugin registers three families of routes on the Joomla API app at https://yoursite.com/api/index.php: a read-only catalog JSON:API (Joomla API token), an authenticated per-user Learner API (rotating token from /login), and a Teacher API for course authoring (same login token + Teacher-group membership). All paths below are relative to https://yoursite.com/api/index.php.
Note — auth, HTTPS & mobile. Endpoints come online automatically once the Web Services plugin is enabled — no manual routing. Learner endpoints must be called with No Auth + the X-Adhrit-Token header only; do NOT place the adhrit token in Authorization: Bearer (Joomla's API layer authenticates that header itself and returns 403). Catalog endpoints use X-Joomla-Token. Toggle each surface, set a token TTL and Require-HTTPS under Components → Adhrit LMS → Options → API Settings, and export a ready-made Postman collection from there. HTTPS enforcement is a backstop — if the web server redirects HTTP→HTTPS the app only sees the upgraded request, so enforce HTTPS at the server (redirect + HSTS). Full setup, Global-Config checklist and secure mobile token storage are in the separate Adhrit LMS — API Integration Guide.

Catalog API — read-only (auth: X-Joomla-Token)

Method Path Auth header Purpose
GET /v1/adhritlms/courses X-Joomla-Token List courses (JSON:API); ?category={id} to scope. Each course carries teacher_ids, chapter_count, lesson_count, review_count, rating_avg, has_final_quiz, currency, price_display
GET /v1/adhritlms/courses/{id} X-Joomla-Token A single course (same enriched fields)
GET /v1/adhritlms/courses/{id}/curriculum X-Joomla-Token One-call nested curriculum: course + chapters → ordered lessons (is_preview, duration, type, has_quiz). Token-authenticated; no course enrolment required
GET /v1/adhritlms/coursescategories X-Joomla-Token List course categories (+ /{id}); each carries course_count
GET /v1/adhritlms/chapters X-Joomla-Token List chapters (+ /{id}); ?course={id} to scope; carry adhritlms_course_id + lesson_count
GET /v1/adhritlms/lessons X-Joomla-Token List lessons (+ /{id}); ?course={id}&chapter={id} to scope; carry both FKs + has_quiz
GET /v1/adhritlms/teachers X-Joomla-Token List teachers (+ /{id}); carry course_ids
GET /v1/adhritlms/speakers X-Joomla-Token List speakers (+ /{id}); carry event_ids
GET /v1/adhritlms/events X-Joomla-Token List events (+ /{id}); carry speaker_ids

Learner API — per-user (auth: X-Adhrit-Token from /login)

Method Path Auth Body / params Purpose
POST /v1/adhritlms/login none {"username":"user","password":"pass"} Sign in; returns a rotating bearer token
POST /v1/adhritlms/logout X-Adhrit-Token — Invalidate the current token
GET /v1/adhritlms/me X-Adhrit-Token — Profile, avatar, preferences
POST /v1/adhritlms/me/preferences X-Adhrit-Token {"categories":[1,4,7]} Save favourite categories
GET /v1/adhritlms/me/orders X-Adhrit-Token — All of the learner's orders (number, course, amount, status, date)
POST /v1/adhritlms/me/avatar X-Adhrit-Token multipart: avatar=@file Upload avatar (JPG/PNG/WebP ≤ 2 MB)
GET /v1/adhritlms/me/courses X-Adhrit-Token — Enrolled courses + progress %
GET /v1/adhritlms/me/courses/{id}/curriculum X-Adhrit-Token — Chapters+lessons with completed/quiz/locked flags
GET /v1/adhritlms/lessons/{id}/content X-Adhrit-Token — Full lesson (enrolment / preview gated)
POST /v1/adhritlms/courses/{id}/enroll X-Adhrit-Token — Free enrols instantly; paid returns 402
POST /v1/adhritlms/lessons/{id}/complete X-Adhrit-Token — Record progress; may issue certificate
GET /v1/adhritlms/quiz?course_id=N X-Adhrit-Token (+ &lesson_id=N | &chapter_id=N) Questions WITHOUT correct answers; each carries lesson_id/chapter_id/scope. Add &lesson_id=Y for one lesson's quiz
POST /v1/adhritlms/quiz/submit X-Adhrit-Token {"course_id":N,"lesson_id":N,"answers":{"12":"B","15":["A","C"]}} Server-side scoring; stores result
POST /v1/adhritlms/orders X-Adhrit-Token {"course_id":N,"payment_method":"paypal|upi|bank","coupon_code":""} Create paid order → payment instructions
GET /v1/adhritlms/me/orders/{id} X-Adhrit-Token — Poll an order's status
GET /v1/adhritlms/me/invoices X-Adhrit-Token — The learner's invoices
GET /v1/adhritlms/me/certificates X-Adhrit-Token — The learner's certificates
GET /v1/adhritlms/me/wishlist X-Adhrit-Token — Wishlist courses
POST /v1/adhritlms/me/wishlist/{id} X-Adhrit-Token — Toggle a course in the wishlist
PUT /v1/adhritlms/me/wishlist/{id} X-Adhrit-Token — Add a course to the wishlist (idempotent)
DELETE /v1/adhritlms/me/wishlist/{id} X-Adhrit-Token — Remove a course from the wishlist (idempotent)

Teacher API — course authoring (auth: X-Adhrit-Token; user must be in the Teacher group)

Uses the same login token as the Learner API. Every route is scoped to the teacher's own items (created_by); teachers can't touch another teacher's content and can't create categories. Master switch: Options → API Settings → Enable Teacher API (off → all rows below return 403 and the Teacher folder is dropped from the Postman export). Send Accept: */* — NOT application/json (Joomla's API replies 406 to it on these routes).
Method Path Auth Body / params Purpose
GET /v1/adhritlms/teacher/courses X-Adhrit-Token — List the teacher's own courses
POST /v1/adhritlms/teacher/courses X-Adhrit-Token {"title":"...","short_description":"...","price":0,"level":"beginner"} Create a course (only admin-enabled fields saved) → course_id
GET /v1/adhritlms/teacher/courses/{id} X-Adhrit-Token — One owned course + its chapters, lessons, quiz
PUT /v1/adhritlms/teacher/courses/{id} X-Adhrit-Token {"title":"...","price":19.99} Update an owned course
DELETE /v1/adhritlms/teacher/courses/{id} X-Adhrit-Token — Delete an owned course (only if admin allows teacher deletion)
POST /v1/adhritlms/teacher/chapters X-Adhrit-Token {"adhritlms_course_id":N,"title":"...","ordering":0} Add/update a chapter in an owned course
POST /v1/adhritlms/teacher/lessons X-Adhrit-Token {"adhritlms_course_id":N,"adhritlms_chapter_id":M,"title":"...","description":"...","lesson_type":"video","video_url":"..."} Add/update a lesson
POST /v1/adhritlms/teacher/questions X-Adhrit-Token {"adhritlms_course_id":N,"question":"...","question_type":"single","marks":1,"choices":[{"text":"A"}],"correct_row":0} Add/update a quiz question (chapter/lesson bound)
DELETE /v1/adhritlms/teacher/items/{id}?type=chapter|lesson|question X-Adhrit-Token — Delete an owned chapter/lesson/question (admin-permitting)
POST /v1/adhritlms/teacher/courses/{id}/image X-Adhrit-Token multipart: image=@file Upload course image (PNG/JPG ≤ 500 KB) into the teacher's folder
POST /v1/adhritlms/teacher/courses/{id}/pdf X-Adhrit-Token multipart: pdf=@file Attach a course PDF (≤ 2 MB)
GET /v1/adhritlms/teacher/profile X-Adhrit-Token — Get the teacher's own profile
PUT /v1/adhritlms/teacher/profile X-Adhrit-Token {"title":"...","designation":"...","email":"...","website":"https://..."} Update own profile (social links validated)
GET /v1/adhritlms/teacher/preferences X-Adhrit-Token — Get preferred course categories
PUT /v1/adhritlms/teacher/preferences X-Adhrit-Token {"categories":[1,4]} Save preferred categories
GET /v1/adhritlms/teacher/courses/{id}/export X-Adhrit-Token — Export an owned course as JSON (media links omitted unless admin enables)
POST /v1/adhritlms/teacher/import X-Adhrit-Token {"format":"adhritlms-course","course":{...},"chapters":[],"lessons":[],"quiz":[]} Import a course from JSON (creates a course the teacher owns)

Testing in Postman — step by step

1
Enable the API on the site

In Joomla admin, go to System → Manage → Plugins and enable: Web Services - Adhrit LMS (registers the routes), Web Services - Joomla, and API Authentication - Web Services Joomla Token. The API app answers at https://yoursite.com/api/index.php. If that URL 404s, confirm the /api/ app is reachable (URL rewriting / .htaccess) — the catalog endpoints render at /api/index.php/v1/adhritlms/....

2
Create a Joomla API token (catalog endpoints only)

The read-only catalog JSON:API uses a standard Joomla token. Open Users → Manage → (your user) → Joomla API Token tab, click Generate, and copy the token. This is only needed for the catalog routes; the Learner API uses its own token from /login.

3
Set up a Postman environment

Create an environment with three variables: base_url = https://yoursite.com/api/index.php, joomla_token (from step 2), and adhrit_token (filled automatically in step 5). Using variables lets every request read {{base_url}} and the right token.

4
Test a catalog request

New request → GET {{base_url}}/v1/adhritlms/courses. Headers: Accept: application/vnd.api+json and X-Joomla-Token: {{joomla_token}}. Send — you should get a JSON:API list of courses. Append /1 to fetch a single course.

5
Log in to the Learner API and capture the token

New request → POST {{base_url}}/v1/adhritlms/login. Headers: Content-Type: application/json, Accept: application/json. Body → raw JSON: {"username":"yourlogin","password":"yourpass"}. The response is {"success":true,"token":"...","user":{...}}. In the request's Scripts → Post-response tab add: pm.environment.set('adhrit_token', pm.response.json().token); so the token is stored automatically. (Each login rotates the token — the previous one stops working.)

6
Call an authenticated endpoint

New request → GET {{base_url}}/v1/adhritlms/me. Set Authorization = No Auth and add the header X-Adhrit-Token: {{adhrit_token}}. Send — you get the signed-in learner's profile. Every Learner-API row in the table above works the same way: No Auth + that one header. Important: do NOT put the adhrit token in Authorization: Bearer — Joomla's API layer authenticates the Authorization header itself and will reject a non-Joomla token there with a 403 before the endpoint runs. The adhrit token belongs only in X-Adhrit-Token.

7
Walk a real learner flow

Try, in order: GET /me/courses → GET /me/courses/{id}/curriculum → GET /lessons/{id}/content → POST /lessons/{id}/complete. For assessments: GET /quiz?course_id=N then POST /quiz/submit with a body like {"course_id":N,"answers":{"12":"B","15":["A","C"]}}. For buying: POST /courses/{id}/enroll (free) or POST /orders (paid → returns PayPal URL / UPI intent / bank details), then poll GET /me/orders/{id}.

8
Author a course as a teacher

The Teacher API uses the very same adhrit_token from step 5 — no separate login — but the signed-in user must be in the Joomla Teacher group (else 403). Try: GET /teacher/courses → POST /teacher/courses (returns course_id) → POST /teacher/chapters and /teacher/lessons (send adhritlms_course_id) → POST /teacher/questions with choices + correct_row → upload a cover with POST /teacher/courses/{id}/image (Body → form-data, key image, type File). Set header Accept: */*. The generated Postman collection already contains a Teacher (adhrit token) folder with all of these wired up.

9
Common responses & tips

401 = missing/expired token (log in again — tokens rotate). 402 on enroll = the course is paid, use POST /orders instead. 403 on a teacher/* route = the user isn't in the Teacher group, or the Teacher API is switched off. 406 (Could not match accept header) = you sent Accept: application/json on a custom route — use Accept: */*. Quiz answers are never returned by GET /quiz; scoring happens server-side on submit. Use application/vnd.api+json on catalog calls.

E1

Platform, Architecture & Packaging

Adhrit LMS is a native Joomla 5/6 component (com_adhritlms) built on the modern MVC stack (PSR-4 namespace Adhrit\Component\Adhritlms, service-provider bootstrapping, namespaced Models/Tables/Views, a SEF RouterView, and separate admin / site / api applications). Content lives in 18 #__adhritlms_* tables. The whole suite ships as one upgrade package that also carries three site modules and the Web Services plugin. An idempotent install script creates tables and back-fills any missing columns on every update, so upgrades never require manual SQL.

US 1.1 Component bootstrap & routing · Platform
The component registers its admin, site and API entry points, PSR-4 namespace and SEF router; each site view is registered with the RouterView so URLs are clean and Itemid-aware.
Task Technical work Progress
Manifest, namespace, service providers admin/adhritlms.xml, admin/services/provider.php, site/services/provider.php DONE
SEF router with exact view matching site/src/Service/Router.php; AdhritlmsHelper::getItemId() exact match DONE
API application entry admin/adhritlms.xml <api> block → api/src DONE
US 1.2 Install, schema & migration · Platform
On install the component creates all tables; on every install/update it back-fills any missing columns idempotently (checks information_schema before ALTER).
Task Technical work Progress
Install/uninstall SQL (18 tables) admin/sql/install/mysql/install.sql, sql/uninstall/mysql/uninstall.sql DONE
Idempotent migration script.php Com_AdhritlmsInstallerScript::ensureTables()/ensureColumns() DONE
Table classes + aliases admin/src/Table/*.php — setColumnAlias('published','enabled'); JSON encode of subform values in check() DONE
US 1.3 One-step package · Platform
A single package ZIP installs the component, the three site modules and the Web Services plugin; each can also be installed standalone.
Task Technical work Progress
Package manifest pkg_adhritlms.xml — component + catalog + recommended + carousel modules + webservices plugin DONE
Build pipeline (lint/validate/scan) php -l, simplexml validate, INI parse, forbidden-string scan on every build DONE
E2

Admin Dashboard & Navigation

Every admin management page is wrapped in a grouped, collapsible dark sidebar rendered by AdhritlmsHelper::renderAdminSidebar(). The Dashboard (Cpanel view) is a modular, responsive dark board of KPI cards, a sales chart and recent-activity tables. Menu groups are opt-in and the sidebar collapses to a mobile drawer under 900px.

US 2.1 Dashboard — Cpanel view · Admin view
The landing dashboard: summary cards, a filterable Sales-Trends line chart, an order-status donut, a KPI strip, quick-shortcut buttons and side-by-side Recent Orders / Recent Courses tables.
Columns / controls:
  • Summary cards: Courses, Orders, Users, Revenue
  • Sales-Trends chart — filters: date range, course, category
  • KPI strip: orders & revenue this month, avg order value, certificates issued, lessons completed, reviews
  • Order-status donut (pending / completed / cancelled)
  • Shortcut buttons: Settings, Course Editor, Courses, Lessons, Quiz, Orders, Reviews, Events, Speakers, Teachers
  • Shared KPI filter: Year / Month / Course / Category (defaults to current month)
Task Technical work Progress
Dashboard view + data helpers admin/src/View/Cpanel/HtmlView.php, admin/tmpl/cpanel/default.php; dashboardKpis(), salesTrend(), orderStatusBreakdown(), recentOrdersDetailed(), recentCoursesDetailed(), kpiYearOptions() DONE
Charts via Chart.js (CDN) donut + trend line; responsive Bootstrap grid blocks DONE
US 2.2 Component sidebar & menu visibility · Admin view
A grouped sidebar (Course Management, Talent Management, Events, Order Management) with Dashboard & Course Editor quick buttons, Backup & Restore and Settings pinned at the bottom. Groups are opt-in via General settings; the current page's group always shows.
Columns / controls:
  • Quick buttons: Dashboard, Course Editor (unique icons) + shrink toggle
  • Course Management: Courses, Categories, Chapters, Lessons, Quiz Questions, Quiz Results, Certificates
  • Talent Management: Teachers
  • Events: Events, Event Categories, Speakers
  • Order Management: Orders, Invoices, Coupons, Reviews
  • Pinned: Backup & Restore, Settings
  • Per-group visibility from Settings → General (nav_show_*)
Task Technical work Progress
Sidebar renderer + visibility gating Helper/AdhritlmsHelper.php renderAdminSidebar() / renderAdminSidebarEnd() — raw single-escaped URLs, nav_show_* checks DONE
Collapsible groups + shrink rail scoped dark CSS + adlNavToggle/adlNavMiniToggle, state in localStorage DONE
Stale list-filter guard validateFilterId() resets remembered course/chapter/lesson/category filters pointing at deleted records DONE
US 2.3 Mobile navigation drawer · Admin view
Below 900px the sidebar becomes an off-canvas drawer opened by a floating Menu button with a tap-to-close backdrop and Escape support.
Task Technical work Progress
Off-canvas drawer Helper/AdhritlmsHelper.php adminSidebarAssets() — adlNavMobToggle(), backdrop, media query DONE
E3

Course Content Management

The heart of the LMS: the Course → Chapter → Lesson hierarchy plus categories. Each entity has an admin list view (bulk publish/unpublish/delete, per-row status toggle, search & relational filters) and an admin edit view (its form). Deletions cascade to keep referential integrity, and categories, chapters and lessons support packaged-template CSV import.

US 3.1 Courses — list view · Admin view
Lists all courses with search, category and status filters and bulk actions; price shown in the configured currency; free courses badged; a Backup & Restore panel exports/imports selected courses.
Columns / controls:
  • Checkbox / bulk select
  • Status toggle (enabled)
  • Title (link to edit) + Free badge
  • Category
  • Price (configured currency)
  • Level
  • Featured (star)
  • ID
Actions:
  • Filters: search, category, published state
  • Bulk: publish / unpublish / delete
  • Export selected → JSON or XML (with chapters/lessons/quizzes)
  • Import → restore as new courses
Task Technical work Progress
List view/model/controller admin/src/{View,Model,Controller}/Courses*, admin/tmpl/courses/default.php DONE
Per-course export / import admin/src/Controller/CoursesController.php export() / import() (JSON+XML, id remap, slug dedupe, category match/create) DONE
US 3.2 Course — edit view · Admin view
The full course form (Details + Metadata tabs). When a price is set and Invoice tax rates exist, a live tax-inclusive breakup panel renders under the price fields.
Field Type Purpose
title text Course title (required)
slug text URL alias (auto from title if blank)
adhritlms_coursescategory_id sql Course category (from #__adhritlms_coursescategories)
short_description textarea Card / listing teaser
description editor Full rich-text description
image media Feature image (Media Manager)
price number Regular price (0 with is_free = free)
sale_price number Optional sale price (used when > 0)
is_free radio Free course toggle (still requires login+enrol)
teachers sql Assigned teacher(s), many-to-many
show_teacher radio Show instructor block on the course page
featured_course radio Feature flag (used by modules/filters)
course_level list beginner / intermediate / advanced
course_language text Language label
course_duration text Duration label
what_will_learn textarea Learning outcomes list
requirements textarea Prerequisites list
certificate_enabled radio Issue a certificate on completion
quiz_pass_percentage number Pass mark % for this course's quizzes
quiz_revisit radio Allow retaking a passed quiz
enabled radio Published status (aliased to 'published')
ordering number Manual sort order
meta_title text SEO meta title
meta_description textarea SEO meta description
meta_keywords textarea SEO meta keywords
Task Technical work Progress
Course form + model admin/forms/course.xml, admin/tmpl/course/default.php, admin/src/Model/CourseModel.php (teacher pivot sync, JSON schedule/info decode) DONE
Live invoice tax breakup admin/tmpl/course/default.php — reads inv_taxes, JS subtotal/tax/total panel DONE
Cascade delete + publish CourseModel::delete()/publish() cascade chapters, lessons, quiz questions, teacher links DONE
US 3.3 Course Categories — list & edit · Admin view
Manage the categories used for browsing/filtering; CSV bulk import skips existing titles.
Field Type Purpose
title text Category name
slug text URL alias
description editor Rich description
image media Category image
enabled radio Published status
ordering number Sort order
Task Technical work Progress
List + edit screens admin/src/{View,Model,Controller}/Coursescategor(y/ies)*, admin/forms/coursescategory.xml DONE
CSV import admin/src/Controller/CoursescategoriesController.php importcsv(); media/com_adhritlms/import/categories_template.csv DONE
US 3.4 Chapters — list & edit · Admin view
Ordered sections within a course. The edit form carries a repeatable Resources subform for gated downloadable materials. Deleting a chapter detaches its lessons/quizzes to General.
Field Type Purpose
title text Chapter title
slug text URL alias
adhritlms_course_id sql Owning course
description editor Chapter description (shown on frontend)
resources subform Repeatable downloads — each row: title (text), type (list: pdf/zip/link), file (path), url (external)
enabled radio Published status
ordering number Sort order within the course
Task Technical work Progress
Chapter screens + resources admin/forms/chapter.xml, admin/src/Model/ChapterModel.php (resources JSON decode; delete detach) DONE
Gated resource download site/src/Controller/ResourceController.php — login+enrolment, realpath-locked, PDF/ZIP only DONE
US 3.5 Lessons — list & edit · Admin view
The learning units. The list shows each lesson's course & chapter and supports CSV import; the edit form carries a media-picker attachments subform. Deleting a lesson detaches its quiz questions.
Field Type Purpose
title text Lesson title
slug text URL alias
adhritlms_course_id sql Owning course
adhritlms_chapter_id sql Chapter (or General = 0)
lesson_type list video / text / …
video_url url Video source URL
video_type list youtube / vimeo / mp4
video_duration text Duration label
description editor Lesson body (rich text)
attachments subform Repeatable downloads — each row: title (text), file (media picker), url (external)
is_preview radio Free preview (viewable without enrolment)
enabled radio Published status
ordering number Sort order
Task Technical work Progress
Lesson screens + attachments admin/forms/lesson.xml, admin/src/Model/LessonModel.php (JSON+legacy decode; delete detach) DONE
Lessons CSV import admin/src/Controller/LessonsController.php importcsv(); media/com_adhritlms/import/lessons_template.csv DONE
E4

Course Editor

A single-screen builder (Courseeditor view) that assembles a whole course — chapters, lessons and quizzes — from one tree without hopping between list views. Add/Edit opens the real admin edit form in-place (all fields, full validation) via an iframe modal and returns to the editor on save. All AJAX actions are CSRF-protected and permission-gated, and everything writes to the same tables the list views use.

US 4.1 Course Editor — builder · Admin view
Pick or create a course, then add/edit/delete chapters, lessons (under any chapter or General) and quiz questions from a live tree. A floating quick-action toolbar and back-to-top appear on long courses; editor colours are themeable.
Columns / controls:
  • Course selector + New Course + Full settings + Preview
  • Tree: chapters → lessons, quiz badges, preview/duration markers
  • Toolbar: Add chapter, Add existing chapter, Add lesson, Add existing lesson
  • Per-item: edit / delete / add quiz / reuse quiz
  • Floating bar (on scroll) + back-to-top
Task Technical work Progress
Editor view + AJAX endpoints admin/src/View/Courseeditor/HtmlView.php, admin/tmpl/courseeditor/default.php, admin/src/Controller/CourseeditorController.php (getTree/saveCourse/saveLesson/deleteChapter/deleteLesson/getQuiz…) DONE
In-place real edit form (iframe modal) opens course/chapter/lesson/quiz edit forms; returns on save DONE
Editor theming config.xml ceditor fieldset (ce_text/chapter/lesson/accent/quiz-badge colours + glow) DONE
US 4.2 Content reuse, reorder & map · Admin view
Clone existing chapters/lessons/questions from any course via a searchable multi-select picker (clones are independent copies); drag to reorder chapters and lessons (saved immediately); when no course is selected, a content-map table lists every course with chapter/lesson/quiz counts and an expandable tree.
Task Technical work Progress
Clone picker + drag reorder searchable picker clones with children; drag handlers persist ordering DONE
Content-map overview Courseeditor/HtmlView::buildCourseMap() — counts + expandable tree + Edit shortcut DONE
E5

Assessments & Quiz Gating

Quiz authoring, results management and the server-side gating engine. A question attaches at exactly one scope with priority Lesson → Chapter → Course (final). Gating is enforced on the server so a learner can never skip a required quiz by clicking ahead or pasting a link, and certificates are only issued when the whole course is genuinely finished.

US 5.1 Quiz Questions — list & edit · Admin view
List with course/chapter/lesson filters and columns and CSV bulk import; the edit form authors a single question and pins it to one scope.
Field Type Purpose
title text Short question title (shown in lists)
question editor Rich question body (images/video allowed)
slug text URL alias
adhritlms_course_id sql Owning course (required)
adhritlms_lesson_id sql Lesson scope (0 = none)
adhritlms_chapter_id sql Chapter scope (0 = none)
question_type list single / multiple / truefalse / text
list_answers textarea Answer options, one per line
correct_answer textarea Correct answer(s), comma-separated for multiple
explanation textarea Shown after submission
marks number Points for this question
enabled radio Published status
ordering number Order within the quiz
Columns / controls:
  • Status, Question, Course, Chapter, Lesson, Type, Marks, ID
  • Filters: search, course → chapter → lesson, published
Task Technical work Progress
Questions list/edit + filters admin/src/{View,Model,Controller}/Quizquestion(s)*, admin/forms/quizquestion.xml DONE
Bulk CSV import QuizquestionsController.php importcsv(); media/com_adhritlms/import/quizquestions_template.csv DONE
US 5.2 Quiz Results — list & edit · Admin view
Every attempt with course, student, score and a sortable Passed badge; searchable by course or student; the record is fully editable. (No publish column — results have no enabled state.)
Field Type Purpose
adhritlms_course_id sql Course
adhritlms_chapter_id sql Chapter scope
adhritlms_lesson_id sql Lesson scope
user_id user Student
total_questions number Questions in the attempt
correct_answers number Number correct
score_percentage number Score %
passed radio Pass/fail flag
attempt_number number Attempt sequence
time_taken number Seconds taken
created_on calendar Attempt timestamp
Columns / controls:
  • Course, User, Quiz Score, Passed (badge), Attempt, Date, ID
  • Filter: search by course/student; passed/failed
Task Technical work Progress
Results list/edit + fixes admin/forms/quizresult.xml (created), QuizresultsModel.php (search course/student, passed filter), Table/QuizresultTable.php (no title/slug check) DONE
US 5.3 Quiz taking & server-side gating · Frontend view
The frontend quiz view renders questions and auto-scores on submit; a required lesson/chapter quiz must be passed before progressing; passed quizzes lock unless revisit is enabled; after a lesson quiz the learner can mark complete & go to the next lesson.
Columns / controls:
  • Question card: title, body, options (radio/checkbox/text)
  • Marks badge, Submit
  • Result: score, pass/fail, correct answers, explanation
  • Post-pass: Mark complete & next lesson
Task Technical work Progress
Quiz view + scoring site/src/{View,Controller,Model}/Quiz*, QuizController.php submit(), QuizModel.php submitQuiz() DONE
Server-side gate + revisit AdhritlmsHelper::passedQuizResult(); lesson-player lock logic; per-course quiz_revisit DONE
E6

People & Events

Instructor and speaker profiles and a full events programme. Teachers link to courses many-to-many; speakers link to events; events carry scheduling, a mapped venue and an online-meeting URL. Each has an admin list view and edit view, and a matching frontend directory + single-profile view.

US 6.1 Teachers — list & edit · Admin view
Instructor profiles rendered 1:1 square on the frontend, linked to the courses they teach.
Field Type Purpose
title text Teacher name
slug text URL alias
designation text Role / title
experience text Years / experience label
image media Photo (square)
description editor Biography
email email Contact email
phone tel Phone
website url Website
facebook url Facebook
twitter text Twitter/X
linkedin url LinkedIn
youtube url YouTube
specialist_in textarea Specialisations
enabled radio Published
ordering number Sort order
Task Technical work Progress
Teacher screens + pivot admin/src/{View,Model,Controller}/Teacher(s)*, forms/teacher.xml; #__adhritlms_course_teachers DONE
Frontend directory + profile site/src/View/{Teachers,Teacher} + tmpl DONE
US 6.2 Speakers — list & edit · Admin view
Speaker profiles assigned to events, clickable from the directory.
Field Type Purpose
title text Speaker name
slug text URL alias
designation text Role
company text Company
image media Photo
description editor Bio
email email Email
website url Website
facebook url Facebook
twitter text Twitter/X
linkedin url LinkedIn
enabled radio Published
ordering number Sort order
Task Technical work Progress
Speaker screens + pivot admin/src/{View,Model,Controller}/Speaker(s)*; #__adhritlms_event_speakers DONE
Frontend directory + profile site/src/View/{Speakers,Speaker} + tmpl (rebuilt in 2.1.2) DONE
US 6.3 Events & Event Categories · Admin view
Event CRUD with dates, a mapped physical venue, an online-meeting URL and assigned speakers; categories group events with counts.
Field Type Purpose
title text Event title
slug text URL alias
adhritlms_eventcategory_id sql Event category
event_type list physical / online / hybrid
event_start_date calendar Start date/time
event_end_date calendar End date/time
event_venue text Venue name
event_address textarea Address
event_map_lat text Map latitude
event_map_lng text Map longitude
online_url url Online-meeting link (absolute external)
max_attendees number Capacity
short_description textarea Teaser
description editor Full description
speakers sql Assigned speaker(s)
image media Event image
featured radio Feature flag
enabled radio Published
ordering number Sort order
Task Technical work Progress
Event + category screens admin/src/{View,Model,Controller}/Event(s)*, Eventcategor(y/ies)*, forms/event.xml, eventcategory.xml DONE
Google Maps venue map key/height/zoom from config.xml maps section DONE
Frontend events + filters site/src/View/{Events,Event,Eventcategories,Eventcategory}; search + category + type filter bar DONE
E7

Commerce & Payments

Orders, invoices, coupons and reviews management plus a working checkout. Each purchase writes an order; a successful/approved order unlocks the course. PayPal is completed by a verified IPN (receiver + amount checked); UPI shows a GPay/Paytm/PhonePe deep-link and a QR; bank transfer and direct/offline are supported. An optional manual-approval mode holds paid orders as Pending until an admin approves them.

US 7.1 Orders — list & edit · Admin view
Manage every purchase. The list shows a status badge with per-row Approve/Cancel and a per-row Invoice button; the edit form lets an admin create an order manually (marking it Completed grants access).
Field Type Purpose
order_number text Auto-generated reference
user_id user Buyer
adhritlms_course_id sql Purchased course
order_payment_price number Amount
order_currency text Currency code
payment_method list paypal/upi/bank_transfer/direct/free
transaction_id text Gateway / reference id
payment_status list pending/completed/cancelled
billing_name text Billing name
billing_email email Billing email
billing_address textarea Billing address
coupon_code text Applied coupon
discount_amount number Discount applied
created_on calendar Order date
enabled radio Published/active
Columns / controls:
  • Status badge (Pending/Completed/Cancelled)
  • Order #, Course, Billing name, Price, Method, Reference, Date, ID
  • Per-row: Approve / Cancel / View Invoice
  • Bulk: Approve / Cancel / Delete + Invoice Settings shortcut
Task Technical work Progress
Orders list/edit + actions admin/src/{View,Model,Controller}/Order(s)*, admin/tmpl/orders/default.php, forms/order.xml DONE
Manual order + auto number + approval OrdersController::approve()/cancel(); grants access when completed; payment_manual_approval DONE
US 7.2 Invoices — list & printable invoice · Admin+Site
A dedicated Invoices list (built from orders) under Order Management with publish/unpublish/delete and View buttons; the frontend renders a printable, tax-inclusive invoice configured under Settings → Invoices.
Columns / controls:
  • Invoice # (prefix + order number), Course, Billed to (name/email), Total, Status, View, Date, ID
  • Publish / unpublish / delete (hides the customer's invoice)
Task Technical work Progress
Invoices admin list admin/src/{View,Model,Controller}/Invoices*, admin/tmpl/invoices/default.php DONE
Printable invoice + settings site/src/View/Invoice/HtmlView.php, site/tmpl/invoice/default.php (3 templates, multi-tax tax-inclusive breakdown, print/PDF) DONE
US 7.3 Coupons & Reviews · Admin view
Coupon codes (percent or fixed, with limits/dates and optional per-course scope) and review moderation.
Field Type Purpose
coupon_code text Discount code
discount_type list percent / fixed
discount_value number Amount or %
max_uses number Usage limit
valid_from calendar Start date
valid_to calendar Expiry date
adhritlms_course_id sql Optional course scope
enabled radio Active
— Review —   (second form)
adhritlms_course_id sql Reviewed course
user_id user Reviewer
rating list 1–5 stars
review textarea Review text
created_on calendar Date
enabled radio Published
Task Technical work Progress
Coupon & review screens admin/src/{View,Model,Controller}/Coupon(s)*, Review(s)* (course/rating/status/search filters) DONE
US 7.4 Checkout & working payments · Frontend view
The cart view: order summary, coupon apply, billing details and a payment-method chooser. PayPal redirects and is confirmed by IPN; UPI shows a deep-link button + QR + transaction-ref field; bank/direct place a pending/completed order per settings.
Columns / controls:
  • Order summary + coupon apply
  • Billing: name, email, address
  • Payment methods: PayPal, Bank transfer, UPI, Direct
  • UPI: Pay-via-app deep link, QR, transaction reference
  • Complete Purchase → order + redirect/instructions
Task Technical work Progress
PayPal redirect + IPN + return site/src/Controller/PaymentController.php process()/redirectToPayPal()/ipn()/paypalReturn() — postback verify, receiver+amount checks, txn stored ACTIVE
UPI deep-link + QR site/tmpl/cart/default.php — upi://pay button + qrcode.js; INR amount embedded ACTIVE
E8

Certificates & Verification

Fully customizable completion certificates issued only when a course is genuinely finished (certificates enabled, all lessons complete, all lesson & chapter quizzes passed, final quiz passed). Learners download a PNG/PDF generated entirely in the browser and share to LinkedIn/Facebook/X; a public verification page confirms a certificate by number and is captcha-protected.

US 8.1 Certificate design (Settings) & admin records · Admin view
The Certificates config fieldset designs the certificate; the admin Certificates list/edit manages issued records.
Field Type Purpose
cert_template list Classic / Modern / Elegant / Minimal
cert_accent_color color Accent colour
cert_border_style list solid/double/dashed/dotted/none
cert_border_width number Border thickness (px)
cert_border_color color Border colour
cert_inner_frame list none/line/double/corners/ornate
cert_frame_color color Inner-frame colour
cert_logo media Logo
cert_heading text Heading text
cert_body textarea Body with {name}{course}{date}{score}{number}
cert_number_position list footer / corner / hidden
cert_show_score radio Show quiz score
cert_signatory_name text Signatory name
cert_signatory_title text Signatory title
cert_signature_image media Signature image
cert_show_verify_link radio Show verification link
cert_preview certpreview Preview sample certificate button
Task Technical work Progress
Design settings + preview config.xml certificates fieldset; admin/src/Field/CertpreviewField.php DONE
Issued-record admin admin/src/{View,Model,Controller}/Certificate(s)*, forms/certificate.xml (course, user, number, issue/expiry date, quiz score, enabled) DONE
Issue gating AdhritlmsHelper::maybeIssueCertificate() DONE
US 8.2 Certificate render, download, share & verify · Frontend view
The frontend certificate view renders the design, offers client-side PNG/PDF and social share; the Verify view confirms a number.
Columns / controls:
  • Certificate render (self-contained inline styles)
  • Download Image (PNG) / Download PDF (html2canvas + jsPDF)
  • Share: native, LinkedIn / Facebook / X, copy link
  • Verify page: number input, captcha, result summary (holder/course/date/number)
Task Technical work Progress
Render + export + share site/src/View/{Certificate,Certificates} + tmpl; html2canvas + jsPDF (CDN, lazy), off-screen capture sandbox DONE
Public verification + captcha site/src/{View,Controller,Model}/Verify*; captcha via Joomla form field; signed shared-link token DONE
E9

Settings & Configuration

All component options live in one config (Components → Adhrit LMS → Options), organised into fieldsets. In 3.6.0 the former Security, Profile and Maps tabs were consolidated as titled sections inside General (same field names, same saved values), and menu-visibility switches were added.

US 9.1 General (with Security / Profile / Maps sections) · Settings
Core options plus admin-menu visibility and the merged Security, Profile and Maps sections.
Field Type Purpose
currency list Currency code:symbol
items_per_page number Frontend page size
quiz_pass_percentage number Default pass mark %
certificate_enabled radio Master certificate switch
show_course_rating radio Show ratings
allow_guest_preview radio Allow guest preview
nav_show_coursemgmt radio Show Course Management group
nav_show_talentmgmt radio Show Talent Management group
nav_show_eventsmgmt radio Show Events group
nav_show_ordermgmt radio Show Order Management group
captcha_enabled radio (Security section) captcha on verify form
profile_avatar radio (Profile section) allow avatar upload
google_maps_api_key text (Maps section) Maps key
map_height number (Maps) map height
map_zoom number (Maps) default zoom
Task Technical work Progress
General fieldset admin/config.xml [general] DONE
US 9.2 Payment settings · Settings
PayPal, bank transfer, UPI, direct payment and manual approval.
Field Type Purpose
paypal_enabled radio Enable PayPal
paypal_sandbox radio Sandbox mode
paypal_email email PayPal account email
bank_transfer_enabled radio Enable bank transfer
bank_details textarea Bank details shown to buyer
upi_enabled radio Enable UPI
upi_id text UPI ID (name@bank)
upi_payee_name text UPI payee name
direct_payment_enabled radio Enable direct/offline
payment_manual_approval radio Hold paid orders as Pending
Task Technical work Progress
Payment fieldset admin/config.xml [payment] DONE
US 9.3 Certificates settings · Settings
Full certificate design (see E8).
Field Type Purpose
(see E8)   Template, borders, frame, logo, heading/body, number position, signatory, verify link, preview
Task Technical work Progress
Certificates fieldset admin/config.xml [certificates] DONE
US 9.4 Appearance settings · Settings
Frontend colour overrides and renamable section labels.
Field Type Purpose
course_text_color color Course text colour override
lesson_text_color color Lesson text colour override
attachments_label text Rename 'Attachments' heading
resources_label text Rename 'Resources' heading
Task Technical work Progress
Appearance fieldset admin/config.xml [appearance] DONE
US 9.5 Course Editor settings · Settings
Colours for the admin builder only.
Field Type Purpose
ce_text_color color Editor text
ce_chapter_color color Chapter titles
ce_lesson_color color Lesson titles
ce_accent_color color Accent buttons
ce_quiz_badge_color color Quiz badge
ce_glow radio Neon glow
Task Technical work Progress
Ceditor fieldset admin/config.xml [ceditor] DONE
US 9.6 Invoice settings · Settings
Invoice branding, template, taxes and preview.
Field Type Purpose
inv_logo media Invoice logo
inv_company text Company name
inv_header textarea Header/address
inv_tax_info text Tax registration (GSTIN/VAT)
inv_prefix text Invoice number prefix
inv_template list classic/modern/minimal
inv_accent color Accent colour
inv_taxes subform Tax rows: label + rate %
inv_footer textarea Footer note
inv_preview invoicepreview Preview sample invoice
Task Technical work Progress
Invoices fieldset + fields admin/config.xml [invoices]; admin/src/Field/InvoicepreviewField.php DONE
US 9.7 Permissions · Settings
Standard Joomla ACL for the component.
Field Type Purpose
rules rules Joomla ACL rules for com_adhritlms
Task Technical work Progress
Permissions fieldset admin/config.xml [permissions] DONE
E10

Backup, Restore & Bulk Import

A dedicated Backup & Restore admin view (admin-only, pinned above Settings) downloads the entire LMS (all 18 tables) as one JSON and restores it in two modes; the Courses list exports/imports selected courses as JSON/XML; and Quiz Questions, Lessons and Categories each ship a CSV template with a mapped importer.

US 10.1 Backup & Restore — view · Admin view
Shows live record counts, a full-backup download, and a restore panel with two modes.
Columns / controls:
  • Record-count badges per table
  • Download full backup (JSON of 18 tables)
  • Upload backup file
  • Mode: Create new items (remapped) / Modify existing (upsert by id)
  • Warning + confirm before restore
Task Technical work Progress
Backup view + controller admin/src/{View,Controller}/Backup*, admin/tmpl/backup/default.php — export(), restore() (FK remap, upsert) DONE
US 10.2 Per-course export/import & CSV imports · Admin view
Course-level JSON/XML export/restore plus CSV bulk import for questions, lessons and categories with skip reporting.
Task Technical work Progress
Course export/import CoursesController.php export()/import() DONE
CSV importers Quiz/Lessons/Categories controllers importcsv(); templates in media/com_adhritlms/import/ DONE
E11

Frontend Learner Experience

The public/student side: catalog and category browsing, the course page (enroll), the gated lesson player with progress tracking, checkout, the tabbed My Learning profile hub, quizzes, certificates and verification. It is template-agnostic (Bootstrap 5 utility classes, Bootstrap Icons with currentColor) and mobile-friendly.

US 11.1 Courses catalog & category views · Frontend view
Browse/search courses; category and level filters; sortable; cards show price, a wishlist heart and enrol state.
Columns / controls:
  • Search box
  • Category filter, Level filter, Sort (latest/title/price)
  • Card: image, title, category, price/Free, wishlist heart
  • Category listing: clickable cards with course counts
Task Technical work Progress
Catalog + category views site/src/View/{Courses,Coursescategories,Coursescategory} + tmpl DONE
Wishlist site/src/Controller/WishlistController.php; heart on cards + course/category pages DONE
US 11.2 Course page (enroll) · Frontend view
The single course: hero, price/enroll CTA, what-you'll-learn, requirements, curriculum accordion, instructors, reviews and wishlist. Free courses require login + one-click Enroll for Free; a course click lands here (enroll), not the progress page.
Columns / controls:
  • Hero: title, image, price / Enroll / Buy / Enroll for Free
  • What you'll learn, Requirements
  • Curriculum accordion (Show all / Close all, chapter descriptions, durations, completion marks + legend + progress bar)
  • Instructors block
  • Reviews (avg rating + list) + Leave a Review
  • Wishlist heart
Task Technical work Progress
Course view + enroll site/src/View/Course + tmpl; CourseController.php enroll(); exact getItemId() routing DONE
Reviews submit frontend rating + text (gated to purchasers) DONE
US 11.3 Lesson player · Frontend view
The learning screen: video/text/attachments, mark-complete, and a Course Curriculum sidebar with completion indicators, a template-coloured progress bar, gated chapter resources and quiz locks. On phones the sidebar is an off-canvas drawer.
Columns / controls:
  • Media: YouTube/Vimeo/MP4 or rich text
  • Course title (H3, linked) above Back-to-Course
  • Downloads & Attachments (renamable) + chapter Resources (collapsed, gated)
  • Mark as Complete → next / quiz
  • Sidebar: chapters→lessons, completion check / current / locked, progress bar %, paperclip markers
  • Mobile: floating Curriculum button → slide-in drawer
Task Technical work Progress
Lesson player site/src/View/{Lesson,Lessons}, site/tmpl/lesson/default.php DONE
Mobile curriculum drawer off-canvas slide-in (<768px), backdrop + Escape DONE
US 11.4 Cart / Checkout / Purchases · Frontend view
See E7 US 7.4 for checkout; My Purchases lists the learner's orders.
Columns / controls:
  • Checkout (E7)
  • My Purchases: course, date, amount, status, Continue Learning
Task Technical work Progress
Cart/payment/purchases views site/src/View/{Cart,Payment,Purchases} + tmpl DONE
US 11.5 My Learning — profile hub · Frontend view
A tabbed personal dashboard with seven tabs and an optional avatar upload (admin-gated).
Columns / controls:
  • Tabs: My Courses (progress bars), Wishlist (enrol/remove), Certificates, Invoices (View buttons), Downloads (resources+attachments grouped by course), Preferences (favourite categories), Account (Joomla summary + edit link)
  • Avatar upload (JPG/PNG/WebP ≤ 2 MB) gated by Settings → Profile
Task Technical work Progress
Profile hub site/src/{View,Model,Controller}/Profile*, site/tmpl/profile/default.php DONE
Avatar + preferences ProfileController::uploadAvatar()/savePreferences()/removeAvatar() DONE
US 11.6 Invoice — printable view · Frontend view
Owner/manager-only printable invoice (see E7 US 7.2). Known limitation: the 'Prev' button does nothing when the invoice was opened in a new tab.
Task Technical work Progress
Invoice view site/src/View/Invoice + tmpl DONE
'Prev' from new tab (deferred) link back to profile Invoices tab / window.close() when opener exists IN PROGRESS
E12

REST API & Mobile

A native Joomla Web Services layer for a Joomla-based/Flutter mobile app. The catalog API is read-only JSON:API (Joomla API token). The Learner API is per-user: the app logs in for a rotating bearer token, then reads and writes its own data — all resolved from the token, never a user id in the URL, and gated by the same server-side rules as the website. Quiz answers, other users' data and admin operations are never exposed.

US 12.1 Catalog JSON:API · API
Read-only GET list/item endpoints for courses, categories, chapters, lessons, teachers, speakers, events — enriched so a mobile app can connect the datapoints: foreign keys + relationship id arrays, aggregates, absolute images, list filtering, and a one-call nested course curriculum.
Columns / controls:
  • GET /courses (+/{id}) — teacher_ids, chapter_count, lesson_count, review_count, rating_avg, has_final_quiz, currency, price_display
  • GET /courses/{id}/curriculum — token-authenticated (X-Joomla-Token), nested chapters → lessons (is_preview, duration, type, has_quiz)
  • Scoping: /courses?category=, /chapters?course=, /lessons?course=&chapter=
  • /coursescategories (course_count), /chapters (lesson_count), /lessons (has_quiz + both FKs)
  • /teachers (course_ids), /speakers (event_ids), /events (speaker_ids)
  • All image fields returned as absolute URLs · Auth: X-Joomla-Token (curriculum is public)
Task Technical work Progress
JSON:API controllers/views + enrichment api/src/Controller/*Controller.php, View/*/JsonapiView.php — prepareItem adds relationships/aggregates + AdhritlmsHelper::apiMediaUrl() DONE
List filtering + speakers resource displayList overrides (?course/?chapter/?category); new Speakers controller+view DONE
Public curriculum endpoint api/src/Controller/CatalogController.php curriculum() DONE
Route registration plg_webservices_adhritlms/src/Extension/Adhritlms.php onBeforeApiRoute — +speakers, +curriculum DONE
Robust error handling api/src/Controller/AbstractCatalogController.php (404/400 on item lookups) + ApiBase::execute() global JSON error boundary + body/param validation DONE
US 12.2 Learner API (login + me/*) · API
Login/logout with a rotating token; the learner's own profile, courses, progress, curriculum (with gating flags), enrollment, lesson complete, quiz get/submit, orders (with payment instructions), invoices, certificates and wishlist.
Columns / controls:
  • POST /login, /logout (X-Adhrit-Token)
  • GET /me, POST /me/preferences, /me/avatar
  • GET /me/courses, /me/courses/{id}/curriculum, /me/certificates, /me/invoices, /me/wishlist (+POST toggle)
  • POST /courses/{id}/enroll, /lessons/{id}/complete, GET /lessons/{id}/content
  • GET /quiz, POST /quiz/submit (answers hidden)
  • POST /orders, GET /me/orders/{id}
Task Technical work Progress
Auth + token base api/src/Controller/AuthController.php, ApiBase.php (rotating SHA-256 token) DONE
Me endpoints api/src/Controller/MeController.php DONE
Learning + orders api/src/Controller/LearnController.php (server-side gating reused from site models) ACTIVE
US 12.3 Teacher API (mobile course authoring) · API
The frontend Teacher Portal is mirrored as Adhrit-token endpoints so the mobile app can author courses. It uses the same login token as the Learner API (X-Adhrit-Token); in addition the account must belong to the Joomla Teacher user group, and every route is scoped to the teacher's own items (created_by) — a teacher can never touch another teacher's content and can never create a category. The whole surface has an on/off switch (Settings → API Settings → Enable Teacher API); when off, all teacher/* routes return 403 and the Teacher folder is dropped from the Postman export.
Columns / controls:
  • Courses: GET /teacher/courses, POST /teacher/courses, GET/PUT/DELETE /teacher/courses/{id}
  • Structure: POST /teacher/chapters · /teacher/lessons · /teacher/questions (send adhritlms_course_id); DELETE /teacher/items/{id}?type=chapter|lesson|question
  • Media (multipart): POST /teacher/courses/{id}/image (field image, PNG/JPG ≤500 KB) · /teacher/courses/{id}/pdf (field pdf ≤2 MB)
  • Profile: GET/PUT /teacher/profile · GET/PUT /teacher/preferences
  • JSON exchange: GET /teacher/courses/{id}/export · POST /teacher/import (export omits media links unless the admin enables it)
  • Auth: X-Adhrit-Token: {{adhrit_token}}, No Auth header. Send Accept: */* (NOT application/json, which Joomla's API rejects with 406).
Task Technical work Progress
Teacher API controller api/src/Controller/TeacherController.php — group-gated + ownership-scoped; reuses TeacherportalModel DONE
Routes + on/off + Postman routes in plg_webservices_adhritlms 1.5.0; api_teacher_enabled gate; Teacher folder in the generated Postman collection DONE
Postman testing (steps) 1) Settings → API Settings → Download Postman collection and import it. 2) Set collection vars base_url (e.g. https://site/api/index.php) and run Learner → Login — it captures adhrit_token automatically. 3) Open the Teacher (adhrit token) folder and run any request; the X-Adhrit-Token header is already wired and Accept is */*. 4) For image/PDF uploads pick a file on the Body → form-data image/pdf row. 5) The signed-in Joomla user must be in the Teacher group or requests return 403. DONE
E13

Companion Site Modules

Three template-agnostic site modules surface LMS content in any module position. Each refuses to install without the component and reuses the component's helpers so data and routing stay consistent.

US 13.1 Catalog module · Module
One module that lists any LMS content type or a search box.
Field Type Purpose
content_type list courses/categories/teachers/events/eventcategories/search
grid list 1×1 … 4×4
subtitle textarea Line under title
order list latest/title/ordering
catid sql Course category filter
featured_only radio Featured courses only
search_placeholder text Search box placeholder
show_image radio Show card image
show_description radio Show description
card_cta_text text Card button text
show_cta radio Show footer CTA
cta_text text CTA text
cta_menuitem menuitem CTA target
Task Technical work Progress
Catalog module mod_adhritlmscatalog/ — helper, dispatcher, tmpl, install guard DONE
US 13.2 Recommended Courses module · Module
Personalised recommendations (prefs → enrolled categories; guests → admin picks).
Field Type Purpose
guest_courses sql Hand-picked courses for guests
hide_enrolled radio Hide owned courses
fallback_latest radio Top up with latest
grid list 1×1 … 4×4
subtitle textarea Subtitle
show_image radio Show image
show_description radio Show description
card_cta_text text Card button text
show_cta radio Footer CTA
cta_text text CTA text
cta_menuitem menuitem CTA target
Task Technical work Progress
Recommended module mod_adhritlmsrecommended/ — AdhritlmsRecommendedHelper.php, tmpl (never cached) DONE
US 13.3 Carousel module — Content · Module
Slide source selection and hand-built custom slides. Each custom slide can use an image/video or a solid-colour background, a centred Bootstrap icon (with size) and a custom bullet thumbnail — all optional (empty keeps defaults).
Field Type Purpose
source list courses/featured/recommended/events/speakers/teachers/articles/custom
limit number Slide count
catid sql Course category
article_catid category Article category
slides subform Custom slides — each row: title, subtitle, bg_type (image/color), image, bg_color, video_url, icon (Bootstrap icon), icon_size, thumb (custom bullet thumbnail), menuitem, url, cta_text, caption. Every per-slide extra is optional — empty keeps the default look
Task Technical work Progress
Content providers mod_adhritlmscarousel/src/Helper/AdhritlmsCarouselHelper.php getSlides() — normalised slide shape incl. thumb/bg_type/bg_color/icon/icon_size DONE
Robust images (missing/renamed never breaks) native lazy-loading (Owl lazyLoad off) + per-image onerror → accent placeholder; load/error refresh; visibility safety-net. tmpl/default.php DONE
US 13.4 Carousel module — Layout & Style · Module
Banner/Slim/Small modes, 20 style presets, 20 animations (10 animate.css + 10 module keyframes: Book/Star-Wars/Sci-Fi/Sports/Blur/Cube/Swirl/Parallax/Reveal/TV), banner width, 9 tile fits, and spacing controls (top/bottom margin, side padding).
Field Type Purpose
mode list banner / slim / small
style_preset list 20 style presets
animation list 20 transitions (10 animate.css + 10 module keyframes)
banner_width list classic / full
banner_height number Banner height px
items_desktop/tablet/mobile number Slim items per row
small_fit list 9 tile fits
tile_size number Small tile px
tile_gap number Tile gap px
space_top number Top margin px (negative pulls up to remove template whitespace)
space_bottom number Bottom margin px
pad_x number Side padding px
Task Technical work Progress
Modes, presets, animations tmpl/default.php (.alc-mode/.alc-style1..20/.alc-fit CSS; Owl animateIn/Out + custom @keyframes) DONE
First-load anti-scatter + spacing window.load init, anti-FOUC opacity guard, refresh triggers; space_top/bottom + pad_x; max-width containment DONE
US 13.5 Carousel module — Carousel controls · Module
Playback, 20 Bootstrap-Icon arrow styles (responsive), 11 fully-clickable bullet types with horizontal/vertical orientation and 8 overlay positions, lazy load.
Field Type Purpose
autoplay radio Autoplay
interval number Seconds
speed number Transition ms
smart_speed radio Auto-tune speed
loop radio Loop
hover_pause radio Pause on hover
arrows radio Show arrows
arrow_style list 20 arrow styles (Bootstrap Icons glyphs, responsive)
arrow_size number Arrow px
dots list 11 clickable bullet types: none/dots/dashes/squares/pills/rings/bars/numbers/roman/alpha/thumbs/icons
dots_orientation list horizontal / vertical
dots_position list below + 8 overlay spots (incl. left-center / right-center)
dots_size number Bullet/thumb px
lazyload radio Lazy-load images
Task Technical work Progress
20 arrow styles tmpl/default.php .alc-arr-1..20; navText Bootstrap Icons glyphs (.alc-gi*); mobile shrink media query DONE
Custom clickable pagination own <button> per slide wired via to.owl.carousel; active sync via relative() on changed.owl.carousel — every bullet type navigates (fixes inert numbers/letters/thumbs and the single-stretched-dot bug) DONE
Bullet orientation + positions dots_orientation horizontal/vertical; .alc-dotspos-* incl. left-center / right-center rails DONE
Autoplay progress bar template-coloured .alc-progress synced to autoplayTimeout (planned) IN PROGRESS
US 13.6 Carousel module — Typography, Button & Appearance · Module
Per-element typography, CTA button, renamable labels and accent.
Field Type Purpose
google_font text Google font family
title_font/size/color mixed Title typography
subtitle_font/size/color mixed Subtitle typography
caption_font/size/color mixed Caption typography
text_align list left/center/right
show_button radio Show button
button_text text Text
button_menuitem menuitem Fallback link
button_position list inherit/left/center/right
button_style list solid/outline/pill/link
button_icon text Bootstrap icon
button_font/size/color/bg mixed Button typography & colours
label_free text Rename 'Free'
label_featured text Rename 'Featured'
accent_color color Accent (defaults to template primary)
Task Technical work Progress
Typography/button/labels mod_adhritlmscarousel.xml + tmpl/default.php (--alc-* variables) DONE
E14

Security Scan Report

Point-in-time results of a full security audit of the Adhrit LMS suite (component, three modules and the Web Services plugin) covering SQL injection, XSS, CSRF, broken access control / IDOR, file-upload & path-traversal, authentication & token handling, SSRF / open redirect and unsafe PHP. Method: four parallel source-level audits plus an independent verification pass that re-checked every claim against the source. Outcome: 0 Critical, 0 High, 0 Medium; 5 Low-severity items found and fixed in 4.2.17. The user stories below record each area that was Verified secure (no action needed). This section is a snapshot — it is only regenerated when the scan is re-run.

US 14.1 SQL Injection · Security

Every database query in the component, modules and plugin was reviewed for untrusted input reaching SQL.

Acceptance (no action needed):

  • All identifiers pass through $db->quoteName() and all values through $db->quote() or an (int) cast.
  • No string interpolation of request data into query fragments; the query builder is used throughout.
  • Learner/teacher endpoints resolve numeric route ids via (int) casts only.

Scan result:

CODE
[SQLi] scanned: admin/src, site/src, api/src, plg_webservices, modules
[SQLi] raw-concatenation sinks found ....... 0
[SQLi] quoteName()/quote()/(int) coverage .. 100%
RESULT: PASS - no injectable query paths
Task Technical work Progress
Verified: parameterised queries only Query builder + quoteName/quote/(int) across all apps DONE
US 14.2 Cross-Site Scripting (XSS) & output encoding · Security

Frontend templates were reviewed for unescaped output of user/teacher-authored data. Plain values are escaped with htmlspecialchars(..., ENT_QUOTES); rich-text (course/lesson/quiz HTML) is now additionally passed through a server-side HTML filter on output.

Acceptance (no action needed):

  • Scalar fields are escaped at the point of output.
  • Rich-text is filtered through AdhritlmsHelper::safeHtml() (Joomla InputFilter, xssAuto) which strips <script>, <iframe> and on* handlers while keeping formatting.
  • The event_type fallback is HTML-escaped.

Scan result:

CODE
[XSS] templates reviewed ................... 40+
[XSS] unescaped scalar echoes ............... 0
[XSS] rich-text now filtered via safeHtml() . course, lesson, quiz, event, teacher, speaker
RESULT: PASS - output-filtering hardening added in 4.3.0
Task Technical work Progress
Verified + hardened: output filtering safeHtml() applied to rich-text templates; scalars escaped DONE
US 14.3 Cross-Site Request Forgery (CSRF) · Security

All state-changing frontend actions were checked for anti-CSRF token enforcement.

Acceptance (no action needed):

  • Every mutating controller task calls checkToken(); forms emit HTMLHelper::_('form.token').
  • The teacher portal's GET JSON export still requires a valid form token in the query string.
  • No state change is reachable by a simple cross-site GET.

Scan result:

CODE
[CSRF] mutating controller tasks .......... all guarded by checkToken()
[CSRF] unguarded state changes ............. 0
RESULT: PASS
Task Technical work Progress
Verified: CSRF tokens enforced checkToken() on every write task DONE
US 14.4 Broken Access Control / IDOR · Security

Object references were checked to ensure a user cannot read or mutate another user's data by changing an id.

Acceptance (no action needed):

  • Progress/quiz endpoints gate on course enrolment (userHasAccess()).
  • Teacher portal + teacher API scope every read/edit/delete to created_by = current user.
  • The Adhrit token is never derived from a client-supplied user id.

Scan result:

CODE
[IDOR] learner endpoints - enrolment gate .. enforced
[IDOR] teacher endpoints - ownership gate ... enforced (created_by)
[IDOR] horizontal access attempts ........... blocked
RESULT: PASS
Task Technical work Progress
Verified: ownership + enrolment gates enrolment gate (4.2.17) + teacher ownership (4.3.0) DONE
US 14.5 File Upload & Path Traversal · Security

Every upload path was reviewed for type confusion, oversized files and directory traversal.

Acceptance (no action needed):

  • Uploads validate is_uploaded_file + extension allow-list + MIME + (images) getimagesize().
  • Sizes are capped by admin config: profile 600 KB, teacher image 600 KB, teacher PDF 2 MB.
  • Teacher files are confined to a per-username folder; stored names are randomised.

Scan result:

CODE
[UPLOAD] validators: is_uploaded_file+ext+mime+getimagesize .. present
[UPLOAD] size caps (config) ............ avatar 600KB / img 600KB / pdf 2MB
[TRAVERSAL] path confinement ........... images/adhritlms/teachers//
RESULT: PASS
Task Technical work Progress
Verified + hardened: governed uploads size caps + per-teacher confinement added in 4.3.0 DONE
US 14.6 Authentication & Session Tokens · Security

The API authentication scheme and password handling were reviewed.

Acceptance (no action needed):

  • Passwords verified with UserHelper::verifyPassword (bcrypt); constant-shape failures avoid account enumeration.
  • API token = base64(userId:secret); only sha256(secret) is stored, compared with hash_equals; rotates on login, cleared on logout.
  • Failed API logins are now rate-limited per IP + username (added 4.3.0).

Scan result:

CODE
[AUTH] password verify ......... bcrypt (verifyPassword)
[AUTH] token at rest ........... sha256, hash_equals compare
[AUTH] brute-force throttle .... ENABLED (configurable) - new in 4.3.0
RESULT: PASS
Task Technical work Progress
Verified + hardened: token auth + throttle login rate-limiting added in 4.3.0 DONE
US 14.7 SSRF & Open Redirect · Security

Outbound requests and redirect targets were reviewed for attacker control.

Acceptance (no action needed):

  • Post-action redirects validate the return target with Uri::isInternal() before redirecting (fixed in 4.2.17).
  • No user-controlled URL is fetched server-side; map/embeds are built from rawurlencoded values.
  • External profile links are normalised through externalUrl().

Scan result:

CODE
[REDIRECT] return-target validation .. Uri::isInternal() (fixed 4.2.17)
[SSRF] server-side fetch of user URLs . none
RESULT: PASS
Task Technical work Progress
Verified + fixed: open-redirect guards Uri::isInternal() on all return redirects (4.2.17) DONE
US 14.8 Unsafe PHP / Injection Sinks · Security

The code was scanned for dangerous sinks (eval, dynamic includes, command execution, weak randomness).

Acceptance (no action needed):

  • No eval(), system(), exec(), shell_exec() or user-controlled include.
  • Security-relevant identifiers use random_bytes() (certificate numbers moved off md5(microtime()) in 4.2.17).
  • No unserialisation of untrusted input.

Scan result:

CODE
[PHP] eval/exec/system/shell_exec ..... 0
[PHP] dynamic include of request data .. 0
[PHP] weak randomness for secrets ...... 0 (random_bytes used)
RESULT: PASS
Task Technical work Progress
Verified + fixed: no unsafe sinks certificate number → random_bytes() (4.2.17) DONE
US 14.9 Low-severity findings fixed in 4.2.17 · Security

Five low-severity items surfaced by the audit were fixed before this snapshot.

Acceptance (no action needed):

  • Open redirect via return param in Lessons/Wishlist controllers — validated with Uri::isInternal().
  • Missing enrolment gate on lesson/quiz progress endpoints — gated with userHasAccess().
  • Unescaped event_type fallback — HTML-escaped.
  • Predictable certificate numbers — switched to random_bytes().

Scan result:

CODE
[FIX] LOW  open-redirect (Lessons, Wishlist) ...... FIXED 4.2.17
[FIX] LOW  enrolment gate (Lessons, Quiz, Course) .. FIXED 4.2.17
[FIX] LOW  event_type output escaping ............. FIXED 4.2.17
[FIX] LOW  certificate number randomness ......... FIXED 4.2.17
Remaining Critical/High/Medium: 0
Task Technical work Progress
Fixed: 5 low-severity items see component 4.2.17 changelog DONE
E15

Teacher Portal & Media Governance

A frontend portal for members of the Joomla Teacher user group (created on install) to author and manage their own courses, plus a matching Teacher REST API for the mobile app. Teachers work only on items they own (created_by), upload only into their own per-username media folder under governed size/type limits, and can never create course categories. Which course fields are editable, whether teachers may delete, and whether JSON exports include media links are all admin-configurable.

US 15.1 Teacher user group & access · Settings
Install creates a Teacher user group (child of Registered). Membership unlocks the portal and the teacher API; the component resolves the group at runtime.
Task Technical work Progress
Teacher group + ownership columns script.php ensureTeacherGroup(); created_by back-filled on chapters/lessons/quizzes/questions; teachers.user_id link DONE
Identity + ownership helpers AdhritlmsHelper::isTeacher()/teacherOwns()/teacherCanDelete() DONE
US 15.2 Frontend course editor · Frontend view
A teacher dashboard lists the teacher's own courses with create / edit / content / JSON / delete actions; a course form exposes only the admin-enabled fields; a content editor manages chapters, lessons and quiz questions.
Columns / controls:
  • Dashboard: own courses, publish state, JSON import
  • Course form: title (always) + admin-selected fields; existing-category picker (no create)
  • Content editor: chapters, lessons (with chapter + description), quiz questions
Task Technical work Progress
Portal MVC site/src/Controller/TeacherController.php, View/Teacherportal, Model/TeacherportalModel.php, tmpl/teacherportal/* DONE
Configurable editable fields teacher_editor_fields checkboxes in Settings → Teacher Portal DONE
US 15.3 Media governance · Settings
All uploads are size- and type-limited and confined to the teacher's own folder. Profile pictures (user/teacher/student) are capped at 600 KB.
Field Type Purpose
profile_avatar_max_kb number Max profile picture size (default 600 KB)
teacher_img_max_kb number Max teacher course image (default 600 KB)
teacher_pdf_max_mb number Max teacher course PDF (default 2 MB)
Task Technical work Progress
Governed upload helpers teacherUploadImage()/teacherUploadPdf() → images/adhritlms/teachers/<slug>/ DONE
Profile 600KB cap ProfileController::uploadAvatar honours profile_avatar_max_kb DONE
US 15.4 Own-items only & deletion policy · Frontend view
Teachers can edit and (if enabled) delete only their own courses, chapters, lessons and questions. They can set course-category preferences like a learner and edit only their own teacher profile.
Task Technical work Progress
Ownership enforcement every mutate checks created_by; delete gated by teacher_can_delete DONE
Own profile + preferences saveProfile() (linked teachers.user_id) + savePreferences() DONE
US 15.5 Teacher REST API (mobile) · API
The teacher portal is mirrored as Adhrit-token endpoints so the mobile app can author courses. Every route is Teacher-group gated and ownership-scoped.
Columns / controls:
  • GET/POST /teacher/courses, GET/PUT/DELETE /teacher/courses/:id
  • POST /teacher/chapters|lessons|questions, DELETE /teacher/items/:id
  • POST /teacher/courses/:id/image|pdf, GET /teacher/courses/:id/export, POST /teacher/import
  • GET/PUT /teacher/profile, GET/PUT /teacher/preferences
Task Technical work Progress
API controller + routes api/src/Controller/TeacherController.php; routes in plg_webservices_adhritlms 1.5.0 DONE
US 15.6 JSON course import / export · Frontend view
A teacher can export one of their own courses as JSON and import a course from JSON (creating a course they own). In a teacher's export, image paths and downloadable links are omitted unless the admin enables it.
Task Technical work Progress
Export/import TeacherportalModel::exportCourse()/importCourse(); media stripped unless teacher_json_export_media DONE
Import toggle teacher_json_import master switch DONE
US 15.7 Drag-and-drop content editor · Frontend view
The course editor is a two-pane builder: a responsive left structure panel (chapters → lessons) with drag-and-drop, and a click-to-edit form on the right. Dragging a lesson into another chapter reassigns it; dragging reorders; every change auto-saves. The landing page lists the teacher's courses as catalog-style cards with search + status/pricing filters, and shows a usage guide when empty.
Columns / controls:
  • Left panel: SortableJS tree; drag lessons across chapters; reorder chapters/lessons
  • Right panel: click a chapter/lesson to edit it in place
  • Dashboard: card grid + live search + status/pricing filters + empty-state guide
Task Technical work Progress
Two-pane builder + auto-save tmpl/teacherportal/editor.php (SortableJS via cdnjs) → teacher.reorder → TeacherportalModel::reorder() DONE
Editor 500 fixed removed HTMLHelper::_('editor.editor') dependency; plain filtered field DONE
US 15.8 Teacher profile media, email & website · Frontend view
Teachers can upload a profile picture (admin-toggleable, max 600 KB) and a signature image (max 500 KB). Social links are validated against their platform. Email and website visibility is controlled both globally (admin) and per-teacher; the website shows as a clickable link under the email on the public profile.
Field Type Purpose
teacher_profile_avatar radio Admin on/off for teacher profile pictures
teacher_show_email / show_email radio / column Global + per-teacher email visibility
teacher_show_website / show_website radio / column Global + per-teacher website visibility
Task Technical work Progress
Profile media + validation teacherUploadImage()/teacherUploadSignature(); validateSocialUrl() DONE
Public display clickable website under email on tmpl/teacher/default.php DONE
US 15.9 Teacher API switch · Settings
The Teacher REST API has its own on/off switch, independent of the Learner API. When off, teacher endpoints return 403 and the Teacher folder is dropped from the Postman export. Teacher endpoints authenticate with the same X-Adhrit-Token as learners.
Task Technical work Progress
Teacher API toggle api_teacher_enabled + apiTeacherEnabled() gate; Postman folder gated DONE
US 15.10 Full lesson/chapter authoring & controlled RTE · Frontend view
The frontend editor mirrors the backend content model: chapters have a description and (admin-enabled) resources; lessons have type, video (type/URL/duration), free-preview and attachments — each field admin-toggled; questions attach to a chapter and lesson (lesson list filtered by chapter). Lesson/chapter descriptions use a controlled rich-text editor whose media selector is off unless the admin allows it.
Field Type Purpose
teacher_lesson_fields checkboxes Which lesson fields show: type / video / preview / attachments
teacher_chapter_resources radio Enable chapter description + resources
teacher_lesson_rte / teacher_lesson_media radio WYSIWYG on/off and whether the media selector is allowed
Task Technical work Progress
Editor fields + hierarchy tmpl/teacherportal/editor.php; lessonFields()/normaliseResources(); question chapter+lesson selectors DONE
Controlled TinyMCE cdnjs GPL build, lazy init, media/paste gated by teacher_lesson_media DONE
US 15.11 Single teacher profile (no duplicates) · Frontend view
The portal adopts a teacher's existing backend record instead of creating a second one, so the portal and the public teacher page always edit the same profile. Specialist-in is editable and shown publicly; the admin Teacher form gains a signature upload.
Task Technical work Progress
Adopt existing record profile() matches created_by/email/name and claims via user_id DONE
specialist_in + backend signature portal field + signature_image media field on the admin Teacher form DONE

Issues & Fixes Log

Every requirement and defect opened across the project, newest first, mapped to its Epic/area, the file-level work that resolved it, the release it shipped in, and its status. 100 resolved · 1 active · 1 in progress.
ID Ver Epic / Area Issue / Requirement Technical work Status
I-0v7a 4.7.0 E10/E15 Import Course import (with its chapters, lessons and quiz) needed an explicit choice between overwriting the existing course and importing a fresh copy with new IDs; the teacher import was also dropping the chapter/lesson relationships. New import_mode (new / overwrite) on the admin Courses import and the teacher-portal import (+ mode on POST /teacher/import). Exports now carry reference IDs (_id/_ref/_chapter/_lesson); import re-maps chapter→lesson→quiz so the copy is fully linked. Overwrite updates the course by ID and replaces its content; teachers may overwrite only courses they own. Component 4.7.0. DONE
I-0v6a 4.6.0 E15 Uploads Teacher profile/signature/course-image uploads crashed with Class "Joomla\CMS\Filesystem\Folder" not found on newer Joomla; image type check needed tightening. Folder creation switched to native mkdir + a blank index.html per teacher folder (learner avatar too); teacher images limited to PNG/JPG-JPEG (ext + MIME), 500 KB. Component 4.6.0. DONE
I-0v6b 4.6.0 E12 API / Postman The exported Postman collection returned 406 (Could not match accept header) on teacher/learner GETs because it sent Accept: application/json. Adhrit-token requests in the export now send Accept: */*, which Joomla's API app negotiates cleanly; documented for mobile clients. Component 4.6.0. DONE
I-0v6c 4.6.0 E15 Quiz authoring The frontend question editor lacked answer choices/correct-answer entry, chapter/lesson binding cues, and editing of existing questions. Choices repeater + correct selector (radio/checkbox/text) building list_answers/correct_answer; tree selection binds question chapter/lesson with icons; existing questions listed per selection with edit/delete. Component 4.6.0. DONE
I-0v6d 4.6.0 E8 Certificates Certificate: empty {course}, off-centre signatures/overlap, cramped long course names, few styles/borders, plain-only border in export, weak background. {course} resolved from the course id; signatures centred over their line with independent L/C/R positions; wider body; 12 templates + 8 border styles + optional inline-SVG vector flourishes (export-safe). Component 4.6.0. DONE
I-0v6e 4.6.0 E2 Dashboard / E13 Carousel Admin needed a pending-review course list; carousel needed glow + highlight typography. Dashboard 'Courses pending review' panel (chapters/category/author). Carousel 1.6.0: per-element outer glow (colour+size) and text highlight background with transparency (Word-style). DONE
I-0v5a 4.5.0 E15 Teacher profile Editing the teacher profile in the portal produced a blank public profile (a duplicate teacher record was created), and Specialist In was not editable. TeacherportalModel::profile() now adopts an existing backend teacher row (matched by created_by/email/name) and claims it via user_id instead of creating a second record, so portal + public page share one profile; specialist_in added to the portal form/save. Component 4.5.0. DONE
I-0v5b 4.5.0 E15 Chapter/Lesson authoring The frontend editor was missing chapter description + resources, and lesson type / video / free-preview / attachments, and questions didn't follow the backend chapter→lesson hierarchy. Chapter description + resources (gated by teacher_chapter_resources); lesson lesson_type/video_*/is_preview/attachments gated by teacher_lesson_fields; question form gained chapter+lesson selectors with the lesson list filtered by chapter. Model saveChild() extended + normaliseResources(). Component 4.5.0. DONE
I-0v5c 4.5.0 E15 Rich-text editor Lessons needed a controlled rich-text editor whose media options can be limited from the backend. Controlled TinyMCE (cdnjs, GPL) lazily initialised in the lesson editor; media plugins/toolbar and paste_data_images disabled unless teacher_lesson_media is on; teacher_lesson_rte switches between WYSIWYG and a plain box, with a safe fallback. Component 4.5.0. DONE
I-0v5d 4.5.0 E6/E8 Teacher signature Admins needed to upload a teacher signature from the backend into the teacher's folder. signature_image media field added to the admin Teacher form (directory images/adhritlms/teachers); rendered on certificates per the 4.4.0 teacher-signature toggles. Component 4.5.0. DONE
I-001 4.4.0 E15 Course editor Frontend course editor returned a 500 (Joomla\CMS\HTML\HTMLHelper editor not found) and needed a proper drag-and-drop builder. Removed the WYSIWYG dependency (plain filtered field); rebuilt tmpl/teacherportal/editor.php as a two-pane builder — SortableJS structure tree (drag lessons between chapters, reorder chapters/lessons, auto-save via teacher.reorder) + click-to-edit forms. Dashboard now shows catalog-style cards with search/status/pricing filters and an empty-state guide. Component 4.4.0. DONE
I-002 4.4.0 E12/E15 API Teacher API had no on/off switch; Postman export and X-Adhrit-Token handling needed confirming. New api_teacher_enabled (Settings → API Settings); apiTeacherEnabled() gate in the API teacher controller; Postman export omits the Teacher folder when off and includes all teacher endpoints when on; teacher endpoints validate the same X-Adhrit-Token. Component 4.4.0. DONE
I-003 4.4.0 E15 Teacher profile Teachers needed a profile picture with an admin on/off, validated social links, and admin+per-teacher control over showing email and website (website clickable under the email). teacher_profile_avatar toggle + upload; validateSocialUrl() drops mismatched Facebook/X/LinkedIn links; global teacher_show_email/teacher_show_website + per-teacher show_email/show_website columns; public teacher page shows website as a clickable link under the email. Component 4.4.0. DONE
I-003b 4.4.0 E8 Certificates Certificate download was cropped again; teachers should be able to add their own signature, with admin control over teacher/site signatures and their placement. Enlarged html2canvas capture padding (no more edge crop); teacher signature_image upload (max 500 KB); config cert_show_site_sign/cert_site_sign_position and cert_show_teacher_sign/cert_teacher_sign_position (left/center/right) wired into renderCertificate(). Component 4.4.0. DONE
I-004 4.3.0 E15 Teacher Portal Provide a frontend portal so Teacher-group users can author and manage their own courses (chapters, lessons, quizzes), edit only their own items, delete only when the admin allows it, and set course preferences like a learner — without ever being able to create course categories. New site MVC TeacherController + Teacherportal view/model/templates; Joomla Teacher user group created on install; ownership via created_by; admin-configurable editable fields and delete policy. Component 4.3.0. DONE
I-005 4.3.0 E15 Teacher API Expose the teacher portal to the mobile app. New api/src/Controller/TeacherController.php (Adhrit-token, Teacher-gated, ownership-scoped) with courses/chapters/lessons/questions/profile/preferences/upload/import-export routes. plg_webservices_adhritlms 1.5.0. DONE
I-006 4.3.0 E15 Media governance Profile pictures must be ≤600 KB; teacher course images ≤600 KB and PDFs ≤2 MB (all admin-configurable); teacher uploads must stay in the teacher's own folder. Config profile_avatar_max_kb / teacher_img_max_kb / teacher_pdf_max_mb; teacherUploadImage()/teacherUploadPdf() confine to images/adhritlms/teachers/<slug>/. Component 4.3.0. DONE
I-007 4.3.0 E15 JSON exchange Teachers can import a course from JSON and export their own course; a teacher's export must omit image/download links unless the admin enables it. TeacherportalModel::importCourse()/exportCourse(); media stripped unless teacher_json_export_media; import gated by teacher_json_import. Component 4.3.0. DONE
I-008 4.3.0 E14 Security Implement the audit's recommended hardening. API login rate-limiting (#__adhritlms_login_throttle, configurable), optional HSTS header when HTTPS is enforced, captcha now fails closed on backend error, and rich-text output filtering via AdhritlmsHelper::safeHtml(). Component 4.3.0. DONE
I-020 4.2.16 E12 API The Require-HTTPS enforcement let an HTTP request through when the site auto-redirects HTTP→HTTPS (the client follows the redirect, so PHP only ever sees the upgraded HTTPS request), and it could mis-detect HTTPS behind a TLS-terminating proxy/load balancer. HTTPS detection is now proxy-aware (AdhritlmsHelper::apiIsHttps()): honours X-Forwarded-Proto / X-Forwarded-Ssl / port 443 in addition to a direct TLS connection. Documented that true enforcement belongs at the web server (redirect + HSTS, ideally reject HTTP on /api) since an app-level check cannot see a request the server has already redirected — the app toggle is a backstop. See the separate API Integration Guide. Component 4.2.16. DONE
I-030 4.2.15 E9 Settings / E12 API Admins needed control over the REST API: see whether the Web Services plugin is installed/enabled and its version, turn the Catalog and Learner APIs on/off independently, set a token lifetime, force HTTPS, and export all endpoints to Postman. New Settings → API Settings tab (admin/config.xml [apisettings]): plugin status/version field (admin/src/Field/ApistatusField.php), Catalog/Learner enable toggles, token lifetime (days; 0 = never), Require-HTTPS switch, and a Postman export button (PostmanexportField.php → admin/src/Controller/ApiController.php postman() streams a v2.1.0 collection built by AdhritlmsHelper::postmanCollection()). Enforcement: AdhritlmsHelper::apiBlock() gates every request (HTTPS + surface toggle) via ApiBase::execute() and the catalog controllers; token TTL checked in authUser() (issue time stored at login, cleared at logout). Component 4.2.15. DONE
I-040 4.2.13 E12 API Path {id} routes failed: POST /me/wishlist/6 reached the controller but returned Course id is required (422), because the router's :id variable wasn't landing in the input the way the custom controllers read it. This affected every id-in-path route (wishlist toggle, enroll, lesson content/complete, order status, curriculum, catalog items). Added AdhritlmsHelper::apiRouteId() which reads the input id and, if empty, falls back to the last full numeric segment of the request path (e.g. /me/wishlist/6 → 6, /me/courses/12/curriculum → 12). All custom controllers now resolve the route id via ApiBase::routeId(), and the catalog item controller uses the same helper. Component 4.2.13. DONE
I-045 4.2.10 E12 API Several authenticated endpoints — GET /me/courses, /me/certificates, /me/invoices, /me/wishlist and GET /quiz — returned a 403 Forbidden (Joomla's HTML page, before the controller ran) with a valid X-Adhrit-Token, while /me and /me/preferences worked. There was also no endpoint to list all of a learner's orders, and users suspected the token was expiring. The failing endpoints were the ones built on Site models (getModel('Profile','Site'), getModel('Quiz','Site')), which do not resolve cleanly under the Joomla API application; the working ones used direct DB access. Rewrote me.courses, me.certificates, me.invoices, me.wishlist, learn.quiz and learn.orderStatus to query the database directly (self-contained), and routed the genuinely reused logic (quiz/submit scoring, orders creation/coupons) through the component's own MVCFactory via a new ApiBase::siteModel() so web/app never drift. Added GET /v1/adhritlms/me/orders (full order list). Confirmed the bearer token does not time-expire — it is valid until the next login (rotates it) or logout (revokes it); the 403 was never expiry. Files: api/src/Controller/MeController.php, LearnController.php, ApiBase.php, plg_webservices_adhritlms/src/Extension/Adhritlms.php. Component 4.2.10 / plugin 1.3.0 / package 4.2.10. DONE
I-050 4.2.9 E12 API Error handling was inconsistent across the REST API. Catalog item endpoints (/courses/{id}, /chapters/{id}, /lessons/{id}, /coursescategories/{id}, /teachers/{id}, /speakers/{id}, /events/{id}) returned a raw 500 when the id did not exist instead of a 404, and POST /me/preferences could hang / time out on a wrong body. Some endpoints already returned proper errors, but not all — every endpoint needed to answer with a clear status and message (record-not-found, bad request body, update failure). Added a shared api/src/Controller/AbstractCatalogController.php that all seven catalog controllers extend: displayItem() verifies the row exists (lightweight COUNT on the PK) and returns a clean JSON 404 ({success:false,error}) for a missing id and 400 for a non-numeric/zero id; both displayList() and displayItem() wrap the parent call so any unexpected failure becomes a JSON error with the right status, never a blank 500. Added a global error boundary in ApiBase::execute() so every learner endpoint (auth/me/learn/catalog-curriculum) converts any uncaught Throwable into a clean JSON error instead of hanging. Tightened validation: /me/preferences 400s on a missing/non-array categories (the timeout case), quiz/submit validates answers, and enroll / lesson / complete / orders / me/orders/{id} reject a missing/zero id with 400. Component 4.2.9 / package 4.2.9. DONE
I-055 4.2.8 E12 API The new course-curriculum endpoint had been registered as public (open to anyone), which exposed a course's chapter/lesson structure without any credential. It should be secured with Joomla authentication like the rest of the catalog so only token-holding clients can read it. Re-registered GET /v1/adhritlms/courses/{id}/curriculum with public => false so the Joomla API application enforces a valid X-Joomla-Token before the controller runs (course enrolment is still NOT required — it is catalog/preview data, just credential-gated). plg_webservices_adhritlms/src/Extension/Adhritlms.php; docblocks updated in api/src/Controller/CatalogController.php. Component 4.2.8 / plugin 1.2.1 / package 4.2.8. DONE
I-060 4.2.7 E12 API A mobile developer reported the catalog could not connect the datapoints: GET /courses/{id} returned no chapter or lesson ids, there was no way to fetch just one course's chapters/lessons, teacher/speaker relationships were absent, images came back as relative paths, and there was no single call to render a course's curriculum for a detail/preview screen. In short, the resources existed but nothing tied them together for an app. Enriched the whole catalog layer: (1) new public GET /courses/{id}/curriculum (api/src/Controller/CatalogController.php) returns the course with nested chapters → ordered lessons (is_preview, duration, type, has_quiz) in one call, no enrolment; (2) list filtering ?category / ?course / ?chapter on courses/chapters/lessons via displayList overrides that seed the models' existing filter state; (3) relationship fields — courses expose teacher_ids, teachers course_ids, events speaker_ids, plus a new /speakers JSON:API resource with event_ids; (4) aggregates on courses (chapter_count, lesson_count, review_count, rating_avg, has_final_quiz, currency, price_display), categories (course_count), chapters (lesson_count) and lessons (has_quiz); (5) all catalog image fields resolved to absolute URLs via AdhritlmsHelper::apiMediaUrl(). Files: api/src/View/*/JsonapiView.php, api/src/Controller/*Controller.php, plg_webservices_adhritlms/src/Extension/Adhritlms.php. Shipped in component 4.2.7 / plugin 1.2.0 / package 4.2.7. ACTIVE
I-070 4.2.6 E13 Carousel A course feature image was renamed on the server after the course had been saved, so its URL began returning 404. Instead of that single slide degrading, the entire carousel went blank — every slide, including the ones whose images were perfectly valid. Re-adding the image to the course did not fix it (the browser had cached the 404 and the loader stayed stuck). Removing the image from the course made only that one image-less slide appear while the rest stayed blank. Expected: a missing or renamed image must never break the carousel — the affected slide should fall back gracefully and every other slide must still render and rotate. Root cause: the module used Owl Carousel's built-in lazy-loader, which keeps every lazy image at opacity:0 until it individually fires a load event; a 404 image never fires load, so Owl's loader stalled and never revealed any of the remaining lazy images.
Fix: (1) disabled Owl lazyLoad (lazyLoad:false) and removed the owl-lazy/data-src markup; (2) slides now render a normal <img> with native browser lazy-loading (loading="lazy|eager"), so each image loads independently and one failure cannot affect the others; (3) added an onerror handler on every slide image that clears the broken src and swaps in the accent-gradient placeholder class, so a missing/renamed file degrades to the styled placeholder at the correct height; (4) added the same onerror fallback to thumbnail bullets; (5) attached a refresh to each image's load AND error event so the stage re-measures either way; (6) added a 1200 ms visibility safety-net that force-adds owl-loaded, so the carousel can never stay hidden behind the anti-flash opacity rule if an asset stalls. File: mod_adhritlmscarousel/tmpl/default.php. Shipped in module 1.5.1 / package 4.2.6.
DONE
I-080 4.2.5 E13 Carousel Only the line/dash bullet was clickable; dots showed a single stretched dot; numbers, letters and thumbnails were inert. Replaced Owl native dots with custom-rendered <button> per slide wired via to.owl.carousel; active sync via relative(); every bullet type now navigates DONE
I-081 4.2.5 E13 Carousel Too few bullet styles; no orientation control. 11 bullet types (dots/dashes/squares/pills/rings/bars/numbers/roman/alpha/thumbs/icons) + dots_orientation (horizontal/vertical) + left-center/right-center overlay positions DONE
I-082 4.2.5 E13 Carousel Needed 20 responsive, Bootstrap-compatible arrow shapes (was 15). 5 new arrow presets (solid accent circle, caret tab, double-chevron, docked bottom-right, tall edge bar) using Bootstrap Icons glyphs; responsive shrink on small screens DONE
I-083 4.2.5 E13 Carousel Custom slides needed colour background, an icon, and a custom thumbnail; keep defaults when empty. Per-slide bg_type (image/color), bg_color, icon + icon_size, thumb; helper carries the shape, template renders colour/icon and thumbnail bullets, all optional DONE
I-090 4.2.4 E13 Carousel Tiles/slides scattered on first load, correcting only on refresh (esp. bottom position). Init on window.load (not DOM-ready) + anti-FOUC opacity guard + post-load/image refresh triggers; max-width containment DONE
I-091 4.2.4 E13 Carousel Bullets not clickable — the full-slide content overlay intercepted clicks. pointer-events:none on the content overlay, auto on its own links/buttons DONE
I-092 4.2.4 E13 Carousel Dash bullets merged into one line; too much top whitespace; wanted more presets/animations. One distinct dash per slide with gaps; spacing controls (space_top negative/space_bottom/pad_x); +10 presets (20) and +10 animations (20) DONE
I-100 4.2.3 E13 Carousel Small layout needed sub-layouts + tile size; users could not choose tile shape/behaviour. 9 tile fits (square/autowidth/wide/portrait/circle/rounded/polaroid/center/stage) + tile_size/gap; Owl autoWidth + .alc-fit-* CSS DONE
I-101 4.2.3 E13 Carousel Banner had no edge-to-edge option. banner_width classic/full; .alc-bw-full 100vw breakout DONE
I-102 4.2.3 E13 Carousel Only dots/thumbnails; bullets fixed below the carousel. 5 bullet types (dots/dashes/numbers/alpha/thumbs) + dots_position overlay (6 spots) + dots_size; Owl dotsData DONE
I-103 4.2.3 E13 Carousel Arrows had no style choice. 15 arrow styles + arrow_size + on/off; .alc-arr-1..15 CSS DONE
I-110 4.2.2 E13 Carousel Style preset stuck on #1 regardless of selection. Field 'style' collided with Joomla module-chrome params['style']; renamed to style_preset DONE
I-111 4.2.2 E13 Carousel Small tiles were oversized. Owl autoWidth + fixed tile_size/gap so tiles are exactly the set size DONE
I-120 4.2.1 E13 Carousel Style presets not visibly applying (esp. imageless slides). Presets restyle slide chrome; imageless slides accent-tinted DONE
I-121 4.2.1 E13 Carousel Only banner/slim; needed a dense tile layout. New 'Small' layout type DONE
I-122 4.2.1 E13 Carousel Too few animations (6). Expanded to 10 transitions DONE
I-130 4.2.0 E13 Carousel No carousel/slider module existed. New mod_adhritlmscarousel: banner/slim, 10 presets, 6 animations, sources, typography/button, RTL DONE
I-140 4.1.0 E12 API Mobile app could not access learner data (profile/progress/enroll). Full Learner API: login/token, me/*, enroll, lesson complete, quiz get/submit, orders, invoices, certificates, wishlist DONE
I-141 4.1.0 E7 Commerce PayPal never completed orders (IPN endpoint missing); checkout passed coupon code as price. Implemented ipn() with postback verify + receiver/amount checks; paypalReturn(); fixed coupon-as-price DONE
I-142 4.1.0 E7 Commerce UPI was static text; no way to actually pay. upi://pay deep-link button + QR (qrcode.js) + transaction-ref capture; INR amount embedded DONE
I-150 4.0.0 E13 Modules No personalised recommendations module. New mod_adhritlmsrecommended (prefs → enrolled categories; guest picks; hide-enrolled; top-up) DONE
I-151 4.0.0 E12 API No REST API for mobile. Read-only catalog JSON:API + Web Services plugin route registration DONE
I-152 4.0.0 E11 Frontend Lesson curriculum sidebar stacked above content on phones. Off-canvas slide-in drawer (site) + admin sidebar drawer (<900px) DONE
I-160 3.7.0 E10 Backup No full-site backup/restore. Backup view: full JSON export + restore modes (create-new / modify-existing) DONE
I-161 3.7.0 E10 Backup No bulk import for lessons/categories. CSV importers + templates (lessons, categories) DONE
I-162 3.7.0 E5/E3 trim(null) deprecation on question/lesson/quiz-result edit forms from orphan chapters. NULL-safe CONCAT_WS chapter labels in the sql fields DONE
I-163 3.7.0 E3 Content Deleting a course/chapter/lesson left orphan children polluting dropdowns/maps. Cascade delete: course→children; chapter→detach to General; lesson→detach quizzes DONE
I-170 3.6.1 E2/E3 Lists looked empty after delete/re-import (stale session filter). validateFilterId() resets remembered filters pointing at deleted records (5 list models) DONE
I-180 3.6.0 E5 Assessments Quiz Result edit crashed ('Form::loadForm could not load file'); list warned 'Undefined property $enabled'; search/save crashed on missing columns. Added quizresult.xml; Passed badge instead of publish; search by course/student; Table::check() without title/slug DONE
I-181 3.6.0 E4 Course Editor Empty editor state was blank. Content-map overview table with counts + expandable tree DONE
I-182 3.6.0 E3/E10 No per-course export/import. Courses list export (JSON/XML) + restore-as-new DONE
I-183 3.6.0 E5 Assessments Adding quiz questions one by one was slow. Bulk CSV import with title auto-mapping + skip reporting DONE
I-184 3.6.0 E9 Settings Security/Profile/Maps were separate tabs. Consolidated as sections inside General (same field names) DONE
I-190 3.5.0 E2 Navigation All left-menu items always visible. nav_show_* visibility switches; current-page group always shows DONE
I-191 3.5.0 E7 Commerce No dedicated invoice management. Invoices admin list (publish/unpublish/delete) under Order Management DONE
I-192 3.5.0 E3 Content Admin could not preview invoice tax split on a course. Live tax-breakup panel on the course edit form DONE
I-193 3.5.0 E11 Frontend COM_ADHRITLMS_FIELD_COURSE_LABEL rendered raw on profile/invoice. Added the key (and siblings) to the SITE language ini DONE
I-194 3.5.0 E11 Frontend Invoice 'Prev' button dead when opened in a new tab. Deferred by request; planned link back to profile Invoices tab IN PROGRESS
I-200 3.4.0 E11 Frontend My Learning was basic. Tabbed profile hub (Courses/Wishlist/Certificates/Invoices/Downloads/Preferences/Account) + optional avatar DONE
I-201 3.4.0 E7 Commerce No invoices at all. Printable invoice system + Settings → Invoices (3 templates, multi-tax, preview) DONE
I-202 3.4.0 E11 Frontend Attachments/resources not signposted in the sidebar. Paperclip markers; chapter resources collapsed by default DONE
I-210 3.3.0 E3/E11 Lesson attachments rendered empty for plain-URL values; no media picker. Media-picker attachments subform; JSON + legacy comma decode DONE
I-211 3.3.0 E9/E11 Frontend section headings not renamable. Appearance: attachments/resources label overrides DONE
I-212 3.3.0 E11 Frontend Lesson player didn't show the course name. Course title H3 (linked) above Back-to-Course DONE
I-213 3.3.0 E1/E11 getItemId() substring match attached wrong Itemid (course→courses). Exact view matching DONE
I-214 3.3.0 E11 Frontend No overall progress bar in the player. Curriculum sidebar progress bar (% + X of Y, template colour) DONE
I-220 3.2.0 E3/E11 Chapters could not carry downloadable materials. Chapter Resources subform + gated streaming endpoint (PDF/ZIP, path-safe) DONE
I-221 3.2.0 E5 Assessments Passed quizzes could be retaken freely. Per-course quiz_revisit toggle; enforced on page + submit DONE
I-222 3.2.0 E5 Assessments After a quiz only 'previous lesson' was offered. Mark lesson complete & next lesson from the results screen DONE
I-223 3.2.0 E2 Navigation Sidebar could not be shrunk; group state not restored. Shrink toggle (icon rail) + restore saved open/closed state DONE
I-230 3.1.1 E2 Navigation Sidebar links double-encoded (&amp;) → everything fell back to dashboard. Escape URLs exactly once DONE
I-231 3.1.1 E2 Navigation Sidebar rendered as a white block. Restyled to the dark theme DONE
I-240 3.1.0 E2 Navigation Flat admin menu, hard to scan. Grouped, collapsible sidebar with quick buttons DONE
I-250 3.0.2 E2 Dashboard Dashboard overflowed; KPIs unfiltered. Constrained chart; shared Year/Month/Course/Category KPI filter DONE
I-260 3.0.1 E2 Dashboard No real dashboard; reviews had no admin. Modular dashboard (cards/chart/donut/KPIs); Reviews admin section DONE
I-270 3.0.0 E1/E13 Component installed alone; no module. Package (pkg_adhritlms) + Catalog module; fixed module catid trim(null) notice DONE
I-280 2.5.7 E5 Assessments Quiz gating could be bypassed via sidebar/direct link. Server-side enforcement; locked lessons redirect to the gating quiz DONE
I-290 2.5.6 E11 Frontend After a quiz, Back went to the sales page; no completion indicators. Back returns to lesson flow; completion marks + legend + progress bar DONE
I-300 2.5.5 E4 Course Editor Add buttons lost on scroll. Floating quick-action bar + back-to-top DONE
I-310 2.5.4 E4/E9 Editor colours not configurable. Settings → Course Editor colour controls DONE
I-320 2.5.3 E3 Content Publish state didn't cascade; disabled-chapter lessons leaked. Course→chapter/lesson cascade; hide lessons under disabled chapters; appearance colours DONE
I-330 2.5.2 E3 Content No bulk actions or relational filters on lists. Bulk publish/unpublish/delete; course/chapter/lesson filters + columns DONE
I-340 2.5.1 E11 Frontend Wishlist missing on some pages; free courses skipped enrolment; chapter descriptions hidden. Wishlist on course/category pages; free-course login+enrol; chapter descriptions surfaced DONE
I-350 2.5.0 E4 Course Editor No content reuse or reordering. Clone existing chapter/lesson/question picker; drag reorder DONE
I-360 2.4.4 E8 Certificates Certificate not centered in exports. Explicit centering of heading/body/logo DONE
I-370 2.4.3 E3 Content Missing batch-action strings; hard-coded $ in Courses list. Added strings; currency-formatted prices DONE
I-380 2.4.2 E7 Commerce Order edit crashed (missing forms/order.xml); no manual orders. Added order.xml + manual order creation; method/reference columns; auto order numbers DONE
I-390 2.4.1 E4/E7 Course Editor unreliable; no order management. Real in-place edit forms; order approve/cancel/delete + manual approval DONE
I-400 2.4.0 E4/E11 No single-screen builder; flat curriculum. Course Editor; collapsible curriculum accordion DONE
I-410 2.3.5 E11 Frontend Encoded ampersands broke redirects (verify result didn't load). Raw ampersands across 22 site-controller URLs DONE
I-420 2.3.0-2.3.4 E8 Certificates Certificate export clipping; verify-page captcha/rendering issues. Off-screen capture sandbox; captcha via form field; borderless verify render; inner-frame designs DONE
I-430 2.2.x E8 Certificates No certificate customization / verification / download. 4 templates, borders/frames, logo/signature; client-side PNG/PDF; public verification + sharing DONE
I-440 2.1.x E6/E11 Category cards not clickable; Courses listing fatal ($this->state unset); no wishlist/profile. Clickable category cards with counts; fixed listing; Wishlist + My Learning profile DONE
I-450 2.0.0 E3 Content Two-level only (course→lesson). Chapters: 3-level hierarchy + chapter quizzes + gating DONE
I-460 1.0.0 E1 Platform Initial modernised release for Joomla 5/6. Courses, categories, lessons, quizzes, teachers, events, speakers, orders, certificates, coupons, reviews, payments, dashboard DONE
Reusable requirements template. Send new work as Epic (capability + goal) → User Story ("As a <role>, I want <capability> so that <benefit>" + acceptance criteria + affected view & fields) → Task (file-level technical note + DONE/ACTIVE/IN PROGRESS). Reference a view by name and I will treat its fields as the story's field list.
Adhrit LMS 4.7.0 · com_adhritlms 4.7.0 · modules Catalog 1.0.1 / Recommended 1.0.0 / Carousel 1.6.0 · plg_webservices_adhritlms 1.5.0
Copyright © 2010–2025 Mix Agile. All rights reserved. · GNU GPL v2 or later · This email address is being protected from spambots. You need JavaScript enabled to view it. · https://www.mixagile.com
M
Written By Author

MixAgile.com

Educator, researcher, and contributing author.