First tagged release. Mappings now apply when a user is created or updated, rather than only when the nightly task runs — three separate bugs meant the event observer almost never wrote anything, leaving the scheduled task as the only path that worked.
Sites importing users over web services (core_user_create_users) were hit hardest: profile fields stayed empty until an administrator ran the task by hand.
Fixed
Mappings were not applied on user create or update. Three independent causes, all in the observer:
- It could only ever update an existing
user_info_datarow. The target field was picked out ofprofile_get_user_fields_with_data(), which LEFT JOINs and so returns an object for every defined field whether or not the user has a data row — making the insert branch unreachable.set_field()then matched zero rows and reported success. A newly created user has no row for the target field, so nothing was written. - Wildcard patterns never matched.
preg_quote()ran before*was expanded, so*became\*and the expansion rewrote the escaped character into\.*— a literal dot, repeated.*@example.commatched only strings of dots before the@, never a real address. helper.phpis namespaced but caught the unqualifiedException, which resolves tolocal_profilefield_autofill\Exceptionand does not exist. Its three catch blocks never caught anything, so database errors during CSV import surfaced as fatals instead of degrading gracefully.
The scheduled task and the observer disagreed about what "matches". The task built a bare SQL LIKE, which is case sensitive on PostgreSQL, while the observer falls back to strcasecmp(). A mixed-case mapping applied in real time but never during the nightly run. The task now uses sql_like() with case sensitivity off, and escapes the pattern so a literal _ or % in a mapping stays literal.
The stylesheet restyled the whole site. Moodle concatenates each plugin's styles.css into the site-wide stylesheet, and around 30 rules here used bare Bootstrap or element selectors — .card, .badge, code, .collapse, .card-body .alert. Every card, badge and <code> element in Moodle was affected. All rules are now scoped to the plugin's own pages.
A JavaScript error on every page load. manage.php requested a core/bootstrap AMD module, which does not exist in Moodle (Bootstrap ships under theme_boost/bootstrap/), producing a RequireJS error. The collapse toggles are driven by Bootstrap's data-api, which the theme already loads, so nothing depended on it.
Missing button spacing. Two Bootstrap 5 me-2 utilities, which are undefined in the Bootstrap 4.6.2 that Moodle 4.5 ships, replaced with mr-2.
Changed
- CSV import handles quoted values containing line breaks. The parser previously split the file on newlines before parsing, tearing such a record into two malformed rows unless it happened to be in the first column. CRLF endings and a UTF-8 byte order mark are handled too — the latter being something this plugin's own export produces.
- CSV export and the template are written through core's dataformat API instead of hand-built strings and raw
header()calls, and values are escaped so a spreadsheet cannot evaluate an exported cell as a formula. - The scheduled task streams users rather than loading every match into memory at once. A mapping with a source value of
*expands toLIKE '%'and matches every active user, which on a large site could exhaust memory during the nightly run. - User-facing messages are localisable. Six messages built from hardcoded English now come from the language pack.
- The plugin now declares Moodle 4.5 as its minimum (it previously said 4.4, which is past end of life) and
$plugin->supported = [405, 502].
Added
- A privacy provider. The plugin stores mapping rules only and no personal data; the values it writes live in core tables already covered by core's providers.
- 74 PHPUnit tests, covering the observer, the scheduled task and CSV parsing. Each regression test was verified to fail against the pre-fix code rather than assumed to be meaningful.
- Continuous integration across Moodle 4.5 to 5.2 on both PostgreSQL and MariaDB — 22 jobs. Both engines are tested deliberately: matching runs through
sql_like(), whose case sensitivity and escaping differ between them, so a fix correct on one can be wrong on the other.
Upgrading
Run the scheduled task once after upgrading to backfill users created before this release:
php admin/cli/scheduled_task.php --execute='\local_profilefield_autofill\task\apply_mappings'
After that, new users are populated as they are created. The task also runs nightly at 02:00.
One behaviour change to check. _ and % in a mapping's source value are now literal text rather than SQL wildcards. If any existing mapping relied — even accidentally — on _ matching any single character, it will now match fewer users. A mapping on EMP_1 previously also matched EMP01; it no longer does.
Suspended and deleted users remain outside the scheduled task's scope, which is a deliberate difference from the observer and is pinned by a test.