This maintenance version introduces some minor improvements and a few relevant fixes, stabilizing code while preparing for the upcoming 4.2 release.
The following points are particularly relevant:
- Usage stats: fixed an issue blocking tracking of browsing actions with MySql databases (worked on MariaDB)
- Average course time now allows decimals
- Course type editing: is not possible anymore to change the course type if users are already enrolled to the course. This and other conditions (subscriptions via API or import) led to some inconsistencies in previous versions, with "ghost" subscribed users on master courses converted from elearning to classroom or vice versa. Also, an automatic realignment algorithm ensures consistency between master course enrollments and classroom session enrollments by removing users enrolled only in the master course and adding those enrolled only in sessions. It operates on one or all classroom courses, respects overbooking rules, and safely handles repeated executions without side effects. It is also applied during upgrades to fix legacy inconsistencies. Check technical details below.
Automatic realignment of classroom course enrollments
Context:
In classroom-type courses, enrollment in the "master" course and enrollment in individual sessions/dates are managed in two separate tables. Over time, due to misalignments caused by imports, APIs, migrations, or manual operations, inconsistent states could occur: users enrolled in the course but not in any session, or users enrolled in a session without being enrolled in the parent course.
A realignment algorithm has been introduced to automatically resolve both cases.
Where:
Method SubscriptionAlms::cleanupUsersNotEnrolledInClassroomDates($id_course = null) — formalms/html/appLms/admin/models/SubscriptionAlms.php
Reference commit: 34803f5747fc5dec00c19b6669b36f2ac7e2763e
Scope:
- If a course ID is provided, it operates only on that course.
- If invoked without parameters, it scans all classroom-type courses in the platform.
- E-learning courses and editions are not affected.
The method performs two realignment steps, one in each direction:
Cleanup of “ghost” enrollments from the master course
It identifies users present in the master course (learning_courseuser) who are not enrolled in any course session (learning_course_date_user). These users are unenrolled from the master course, restoring a consistent state.
Automatic enrollment in the master course
It identifies users enrolled in one or more sessions/dates (learning_course_date_user) who are not enrolled in the parent course (learning_courseuser). For each user:
- Retrieves the course levels; if missing, they are created.
- Assigns the user to the student level group.
- Creates the enrollment record in the master course, respecting the overbooking rules configured for the course.
Handled scenarios
| Case | Action |
|---|---|
| User enrolled in the master course but not in any session | Unenrollment from the master course |
| User enrolled in a session but not in the master course | Enrollment in the master course as student, creation of levels if missing |
Properties
- Idempotent operation: it can be executed multiple times without producing duplicates or side effects.
- Overbooking rules are respected, so automatic enrollment in the master course does not bypass configured limits.
- It is also invoked during upgrade procedures to fix legacy inconsistencies in migrating environments.
Full Changelog
---------------------------------------------------------------------------------------
forma.lms 4.1.42
---------------------------------------------------------------------------------------
Release date: 2026 April
- # - fix field value error on profile save
- # - remove classroom edition
- # - feat(course): lock course type dropdown when enrolled users exist
- # - feat(course): pass has_enrolled_users to course mask view
- # - feat(course): block course type change in upCourse() when users are enrolled
- # - feat(course): add hasEnrolledUsers() method to CourseAlms model
- # - fix(lomanager): fix LO copy from organization to homerepo with SCORM DB-only copy
- # - fix: correct POST field types in user profile
- # - fix: change decimal separator from dot to comma
- # - fix: add rtrim in medium_time
- # - chore: bump version to 4.1.42
---------------------------------------------------------------------------------------
forma.lms 4.1.41
---------------------------------------------------------------------------------------
Release date: 2026 March
- # - fix: update point in test.php and set medium_time decimal in learning_course and learning_course_date
- # - fix: add missing lib.course.php include in CoursereportLmsController
- # - fix(report): handle unsaved report filters and ensure JSON migration consistency
- # - fix(report): fix export of unsaved report filters (nosave mode)
- # - chore: bump version to 4.1.41
---------------------------------------------------------------------------------------
forma.lms 4.1.40
---------------------------------------------------------------------------------------
Release date: 2026 March
- # - fix(migrations): resolve stale non-ASCII paths in SCORM package migration
- # - fix findEnrolledCourses in DashboardBlockAnnouncementsLms
- # - fix(metacertificate): fix date and download in MycertificateLms
- # - fix: move query outside foreach in coursestatsLms
- # - chore: bump version to 4.1.40
---------------------------------------------------------------------------------------
forma.lms 4.1.39
---------------------------------------------------------------------------------------
Release date: 2026 March
- # - fix(migrations): add safety checks for file rename and optimize ALTER TABLE
- # - fix(tracking/file upload): enhance validation and error handling for tracking/logs and sanitize file uploads
- # - fix render tab metacertificate
- # - fix(statUser): event handling
- # - chore: bump version to 4.1.39
