8.2 Changelog
This page lists all the changes introduced in 8.2 releases. See also the release notes and the migration guide.
8.2.4 (2026-10-07)
Change
-
Upgrade backend dependencies
Details
Here is the list of backend dependencies upgraded:
-
Shiro from 2.1.0 to 2.2.1
-
pac4j from 6.5.3 to 6.5.9
-
buji-pac4j from 9.1.1 to 9.1.2
-
Tomcat from 10.1.57 to 10.1.60
-
Jackson from 2.21.5 to 2.21.7
-
Groovy from 4.0.32 to 4.0.33
-
Slf4j from 2.0.18 to 2.0.20
-
Ldaptive from 2.5.0 to 2.5.1
-
Jansi from 4.1.0 to 4.1.3
-
Swagger from 2.2.50 to 2.2.55
-
ByteBuddy from 1.18.10 to 1.18.14
-
Resteasy from 6.2.16 to 6.2.19
-
Graal from 25.0.3 to 25.0.4.1.1
-
Hibernate from 6.6.53 to 6.6.58
-
Hibernate Validator from 8.0.3 to 8.0.5
-
Jsoup from 1.22.2 to 1.23.2
-
Redisson from 4.3.1 to 4.7.0
-
Fix
-
Fix forceTitle view param and canEdit dashlet attribute in popup editor for all views
Details
The forceTitle action param was only applied when opening records from a grid dashlet popup. It is now applied for all views (grid, cards, kanban, calendar, gantt) opening the popup editor.
A panel-dashlet declared with both
canEdit="true"andreadonly="true"now lets users edit its records, as the canEdit attribute is meant to override the readonly state. -
Fix scheduled jobs missing for some tenants at startup
Details
In multi-tenancy mode, the schedules of all tenants were loaded in parallel into a non-thread-safe map. Some tenants' schedules could be lost, and their jobs were not scheduled.
-
Fix audit log processing blocked by a failing record
Details
When a record failed with a database error during audit log processing, the whole batch was rolled back, including its retry count. The same record then failed again on every run and blocked the other pending audit logs of its transaction.
A failing record no longer rolls back the others, and its retry count is saved so it is discarded after the maximum number of retries.
-
Fix merging of values returned by chained actions
Details
When several actions were chained together, the values each of them returned were aggregated before being applied to the record, and that merge could corrupt relational values.
New (unsaved) collection rows returned by different actions were all matched on their (missing) id and collapsed into a single row, losing data. Such rows are now matched by their client id (cid) instead, so distinct new lines stay separate.
A many-to-one or one-to-one field pointed at a different record by a later action was deep-merged with the previous value, leaking stale fields from the old record onto the new one. The new value now fully replaces the previous one as soon as the record id changes; values are only merged when they refer to the same record.
-
Allow built-in helpers in Groovy action scripts
Details
Groovy
<action-script>actions threw aScriptPolicyExceptionwhen using built-in scope variables ($request,$response, and transactional$em).ActionRequestandActionResponseare now in the defaultScriptPolicyallow list, and$emis wrapped in an allowedEntityManagerdelegate. JavaScript action scripts were not affected. -
Fix live mail messages sent to the wrong tenant with Redis
Details
In multi-tenant setups with Redisson, live mail messages from other instances lacked tenant context and reached only default-tenant users.
Messages now include the publisher’s tenant. Because the payload structure changed, mixed-version instances will not share live mail during rolling upgrades.
-
Fix panel-dashlet not reloading when its refresh attribute is set
-
Fix date widget portal focus handling in advanced search
Details
Selecting a month or year from the date picker’s dropdown on a date field in Advanced Search could freeze or crash the application.
-
Fix report images loaded from the wrong tenant
Details
In multi-tenancy mode,
ImageToolloadedMetaFilerecords from the default tenant’s database for all reports. Tasks ran onReportExecutorwithout tenant context and reused a default-tenant database session.Tasks submitted to
ReportExecutornow run with the caller’s tenant and close their database session on completion. -
Fix
ContextAwaretasks running on the wrong tenantDetails
In multi-tenancy mode,
ContextAwaretasks running on reused threads (such as thread pools) kept the database session opened by the first task on that thread. As the session is bound to the tenant it was opened with, later tasks ran on the first tenant’s database, whatever tenant was set.Tasks wrapped with
ContextAwarenow run with their own database session, opened on the task’s tenant and closed once the task is completed. Running aContextAwaretask inline for another tenant than the current database session now throws an exception, see the multi-tenancy documentation. -
Pin EclipseLink MOXy as JAXB provider
Details
Call
JAXBContextFactory.createContextdirectly instead ofJAXBContext.newInstanceso classpath provider lookup does not pick another implementation (such as Glassfishjaxb-runtime).Drop
jaxb.propertiesfiles that Jakarta XML Binding 4.0 no longer reads. -
Fix Gantt tasks collapsing to zero duration when dragged in zoomed-out views
Details
Dragging a task bar in the Gantt view could inadvertently set its end date equal to its start date. This occurred when a task was shorter than a single column of the current zoom level (e.g., a 4-day task in the Month view, or a 3-week task in the Year view). Because both edges of the bar snapped to the nearest column independently, they could land on the exact same date.
The end date is now strictly derived from the snapped start date plus the task’s original duration. Moving a task now reliably preserves its length across all zoom levels.
-
Fix cross-thread database access during live mail notifications
Details
New mail messages were sent to the WebSocket mail channel as entities attached to the request’s database session. Building the message details on the channel’s worker thread lazy-loaded data through that session from another thread, which could fail or return incomplete details.
The message is now reloaded on the worker thread.
-
Fix database engine detection when using a JNDI data source
-
Fix Redisson topic listeners retaining database sessions across tenants
Details
Redisson topic listeners run on reused threads. Database sessions opened by a listener leaked across tasks on the same thread, keeping the first message’s tenant.
Listeners now run as
ContextAwaretasks with the publisher’s tenant and a dedicated database session that closes when done. -
Fix thread leak when updating DMS permissions
Details
Each
DMSPermissionRepositoryinstance created its own worker thread to apply permissions to child files, and never released it. Idle threads accumulated over time.The worker thread is now created only when child permissions need updating, and exits once done.
-
Fix detached entity error during audit log processing
Details
Audit processing rebuilt a ContextHandler proxy for tracked reference fields and computed their name, which re-pointed the managed entity through a bidirectional one-to-one wiring setter and failed to cascade on flush. Read the reference label directly from the stored audit map instead, and wire the inverse side of a one-to-one on edit so the name is computed correctly at save time.
-
Fix slow recovery of pending audit logs
Details
The leader recovery only processed a limited number of pending transactions on each run, every few minutes, so a large backlog of pending audit logs took a long time to be recovered.
All pending transactions are now recovered in a single run.
-
Fix time zone of API key dates
-
Fix WebSocket handlers ending a unit of work they did not start
Details
withAuth ignored the failure to begin a unit of work when one was already active, then ended it anyway, closing the caller’s unit of work.
Only end the unit of work if withAuth started it.
-
Fix lock errors on audit logs processing
Details
When several workers processed the audit logs of the same transaction, for example the leader recovery and the asynchronous queue, PostgreSQL reported "could not obtain lock on row in relation audit_log" errors.
Audit logs are no longer locked by rows: an advisory lock on the transaction ID ensures only one worker processes its audit logs at a time, and the other workers skip it without error.
-
Fix DMS child permissions applied before the parent permission is committed
Details
When sharing a folder, a background thread propagated permissions to child files before the folder permission committed. A newly created permission was not yet visible to the worker.
Child permissions are now applied after commit, so the worker sees the saved permission.
-
Fix request scope not opened for scheduled jobs outside multi-tenancy
Details
Jobs run without a tenant didn’t get a request scope, so any
@RequestScopedservice they used failed withOutOfScopeException. -
Fix grid row saving before validIf validation completes
Details
In an editable grid, pressing Enter to save a row could commit a field with a
validIfcondition before its validity was re-evaluated. This allowed an invalid value to be saved, or delayed showing the invalid state until a later action.
Security
-
Added sensitive JDK classes to the script policy deny list
-
Consistently exclude password fields from record data
-
Check the origin of the websocket handshake
-
Fix CORS origin matching and disable credentials by default
Details
With
cors.allow-origin = *, a literalAccess-Control-Allow-Origin: *is now sent and credentials are never allowed with*.cors.allow-credentialsnow defaults tofalse. Set it totrue, with explicit origins, if a browser client served from another origin relies on cookies.cors.allow-originis now a comma-separated list of exact origins (scheme://host[:port]) instead of a regular expression. Invalid values, such as regular expressions, are ignored with a warning: list each allowed origin explicitly. Thenullorigin cannot be listed explicitly: it is only accepted with*, without credentials.This changes the CORS behavior: if
cors.allow-originis configured, check the 8.2 migration guide.
8.2.3 (2026-08-06)
Feature
-
Select field content on focus for input widgets
Details
Numeric (integer, long, decimal), duration, date/datetime/time, uuid, selection, and many-to-one fields now automatically select their entire content when the field receives focus — whether via Tab or a mouse click. A second click on an already-focused field places the cursor normally without forcing reselection.
String fields are excluded, since users editing text most often just want to fix a typo rather than replace the whole value.
-
Add national phone number placeholder flag for phone widget
Details
Phone numbers stored without a country prefix (national format, legacy data) now display an "XX" placeholder flag — a rounded rectangle with two diagonal crossing lines — instead of the default locale country flag, making it clear that the country is unspecified.
The flag uses theme CSS variables so it renders correctly in both light and dark mode. Selecting any country from the dropdown while in XX mode exits national format and converts the value to international format.
Change
-
Upgrade backend dependencies
Details
Here is the list of backend dependencies upgraded :
-
Upgrade Jackson from 2.21.4 to 2.21.5
-
Upgrade Tomcat from 10.1.55 to 10.1.57
-
Upgrade Logback from 1.5.34 to 1.5.38
-
Upgrade PostgreSQL driver from 42.7.11 to 42.7.13
-
-
Disable L2 cache for volatile mail entities
Details
MailMessage,MailFlagsandMailFollowerare no longer second-level cacheable. These entities are either fast-growing and reached via scattered queries (MailMessage) or hold per-user state (MailFlags,MailFollower), so the entity cache had a near-zero hit ratio while adding memory and invalidation overhead.
Fix
-
Fix one-to-many onChange firing on its own when canMove is enabled
Details
When a one-to-many field had
canMove="true"(a valid sequence/order field), itsonChangehandler could fire on its own, without any user interaction with the grid, whenever the field’s value was refreshed (e.g. after a save, a version change, or any action pushing updated items back to the client) while the stored sequence values did not already match the contiguous order the widget recomputes.The internal reordering reconciliation now updates the sequence field without triggering
onChangeor marking the record dirty, consistent with the other internal writes in the widget. -
Fix grid pagination allowing the per-page limit to be bypassed
Details
In a grid view, editing the page size field and decreasing it below 1 (for example to a negative value using the number input’s down arrow) was not rejected, letting the grid load records without respecting the configured maximum number of records per page. The entered page size is now validated against the configured maximum: a value above the maximum is capped to it and a non-positive value falls back to the default page size. When no maximum is configured, any positive value is allowed, so a large value can be entered to fetch all records.
-
Fix incorrect country flags in phone widget
Details
Corrected some country flags that were outdated or incorrect:
-
France: fixed the flag colors.
-
Martinique: replaced the obsolete flag with the current one.
-
Malaysia: fixed the number of stripes.
-
Syria: updated to the current national flag.
-
-
Fix Monaco Editor when redirecting from /index.html
Details
When deployed with a context path (e.g.
/axelor-erp), the CodeEditor widget failed to load Monaco assets becauselocation.pathnameincluded/index.htmlafter the auth redirect, producing a wrong base URL for the Monaco loader.Fixed by changing login URL from
/index.htmlto/. As a failsafe measure in case theindex.htmlendpoint is still used, the Monaco base URL computation now also strips/index.htmlfromlocation.pathnameif present. -
Fix MetaModule data not refreshed when restoring views
-
Fix phone widget marking record dirty on focus without changes
Details
Focusing a phone field and immediately blurring it (without typing anything) incorrectly marked the record as dirty.
-
Compact M2M field items in action context and save requests
Details
When a many-to-many field held full record objects (e.g. filled by an action with records fetched from a repository), all their fields were re-sent on every subsequent save or action call. The server then attempted to update the linked records themselves, which could fail with unexpected permission errors.
Saved many-to-many items are now sent as plain references (id, version and the display name), whether the field belongs to the model or is only defined in the view. New (unsaved) items are still sent in full so they can be created. One-to-many fields are not affected.
The same compaction is applied when an editable grid row is committed or a record is saved from a popup editor, so many-to-many values filled by actions there no longer carry updates into later saves of the parent record.
-
Clear meta caches after database update/init
Details
The
database update/initCLI commands now invalidate the meta caches (actions, translations, …) once modules have been loaded. Previously, when a distributed cache was shared with a running instance, the loaded changes were not reflected until the instance was restarted. -
Fetch missing relation fields via selector and editor popups
Details
When selecting a record from a selector popup or saving via an editor popup in M2O, M2M (tags), or M2M/O2M collection grid widgets, fields required for display (e.g. target-name, colorField, computed grid columns) that were not present in the popup’s grid or form view could be missing, causing values to disappear until the page was refreshed.
Records returned from these popups now always include the fields the widget needs for display, so values no longer disappear after selecting or saving a record.
-
Fix form tab focus steal
Details
When a form button ran
save,<action-open-in-tab>on a new record, the newly opened tab immediately lost focus and the original form tab was re-activated.When the view detects the active tab’s path changed, it checks if the change was caused by an internal tab-state redirect. If so, it does not re-queue a
tabs.openfor the same path. -
Fix multiple line selection in grid on macOS
Details
Selecting multiple lines in the grid did not work on macOS because the keyboard event was mapped to the wrong modifier key. The correct key is now used so that range/multi-selection works consistently across platforms.
-
Fix validIf state being overridden by other field updates
Details
A field marked invalid through
validIfcould silently go back to looking valid, even though nothing about that field itself changed. This happened whenever another field elsewhere on the same form reacted to the same change, for example by showing or hiding itself. As a result, an invalid value could go unnoticed, and saving the record could also fail with an unexpected error. -
Fix add sub-item button shown when canNew is false
Details
In a tree grid, the sub-tree header (holding the title and the "add sub-item" button) was still displayed for empty nodes even when
canNewwas false, letting users trigger record creation that was not permitted. -
Fix panel header color not applied to title text
-
Fix JSON model fields not showing in selector popup grid
Details
When opening a selector popup for a JSON model (M2O, M2M tags, or M2M/O2M collection grid), the grid view was missing its JSON model fields, causing all custom field columns to appear empty.
The selector popup grid now correctly resolves the JSON model, so custom fields display as expected.
-
Fix grid date search not matching single-digit month or day
Details
Typing a date filter with a single-digit month or day in a grid column search (e.g.
6/2026for June 2026) produced no results. This was caused by dayjs requiring exactly two digits forMM/DDformat tokens, unlike moment.js which accepted a single digit. -
Fix phone widget country dropdown dark mode
Details
The country selector dropdown had hardcoded light-mode styles (white background, whitesmoke hover) that made it unusable in dark mode.
The dropdown now uses theme CSS variables so it adapts automatically to the active theme.
8.2.2 (2026-06-18)
Feature
-
Apply default Hibernate second-level cache region settings with Redisson
Details
When using a Redisson-based Hibernate cache region factory, default cache region settings equivalent to the built-in Caffeine defaults are now applied: max entries and time-to-live for the entity/collection/naturalid and query regions, plus dedicated settings for the frequently-read User, Group, Role, and Permission regions. With the native region factory, only time-to-live settings are applied, as it doesn’t support max entries and max idle time.
Any
hibernate.cache.redisson.*settings defined in the application configuration take precedence over these defaults. -
Enable L2 collection cache for auth association collections
Details
The auth association collections
User.roles,User.permissions,Group.roles,Group.permissionsandRole.permissionsare now stored in the second-level collection cache (READ_WRITE). Their target entities are already cacheable, so reading these collections no longer issues a SQL query against the link table on every load. -
Add distributed publish/subscribe topic
Details
DistributedFactory.getTopic(name)now provides anAxelorTopic, a publish/subscribe primitive for broadcasting messages to all application instances. Messages are delivered to every listener, including those registered on the publishing instance.In a distributed setup, messages are broadcast through the cache backend (e.g. Redis when using Redisson); in a single-instance setup they are dispatched in-process. The current tenant is carried with each message and restored before the listener runs.
Change
-
Use cache loader only for read-through access
Details
The cache loader used to be forced on
get(key, mappingFunction)and theasMap()view as well, so that loading behaved consistently with the Redisson MapLoader. Now that the Redisson MapLoader is no longer used, that consistency layer is unnecessary and has been removed.The loader is therefore consulted only on read-through access through
get(key)andgetAll(keys). Theget(key, mappingFunction)method and theasMap()view follow standard Caffeine/Redisson semantics and no longer invoke the loader: the mapping function is used as-is and the map view does not trigger loading. -
Upgrade backend dependencies
Details
Here is the list of backend dependencies upgraded :
-
Upgrade Logback from 1.5.32 to 1.5.34
-
Upgrade Jackson from 2.21.3 to 2.21.4
-
Upgrade ByteBuddy from 1.18.8 to 1.18.10
-
Upgrade Hibernate from 6.6.50 to 6.6.53
-
Upgrade Pac4j from 6.5.1 to 6.5.3
-
-
Improve translation lookup performance with distributed cache providers
Details
Translation lookups no longer fetch the whole translation bundle from the (possibly distributed) cache on every key access. A node-local cache now fronts the shared
messagescache. This can noticeably speed up translation-heavy paths when using a distributed cache provider.Translation changes are propagated to other nodes immediately through a distributed topic that invalidates their node-local caches, with the local cache TTL (10 minute) acting as a fallback.
Fix
-
Fix potential connection pool deadlock with Redisson cache loader
Details
Redisson caches no longer rely on Redisson MapLoader for read-through loading. With MapLoader, the loader runs on Redisson threads while the caller blocks on
RedissonMap.get→CompletableFuture.getwithout timeout. A loader that accesses the database consumes an extra connection, which could deadlock when the connection pool is exhausted, for example by ParallelTransactionExecutor already consuming all connections.The cache loader is now relying on
RMap.computeIfAbsent, which executes on the caller thread. -
Set
read-writeas the default second-level cache concurrency strategyDetails
The default Hibernate second-level cache concurrency strategy is now explicitly set to
read-writefor all cache providers. Entities annotated with@Cacheablebut without an explicit@Cachestrategy previously fell back to the strategy reported by the region factory, which differs between providers: Caffeine/JCache defaults toread-write, but Redisson defaults totransactional.The
transactionalstrategy relies on a JTA transaction manager to roll the cache back together with the database. Since the application uses resource-local transactions, a rolled-back update was never undone in the cache, leaving a stale (higher) entity version in Redis. Subsequent updates of the same record then failed with an optimistic-lock error. Forcingread-writemakes the behavior consistent and correct across all providers. -
Move to perform early
-
Scope distributed locks and atomic longs by tenant
Details
Distributed locks and atomic longs obtained from
DistributedFactoryare now scoped to the current tenant when multi-tenancy is enabled. The distributed backend (e.g. Redis) is shared across tenants, but the resources these guard (such as module initialization and meta restore) act on per-tenant databases. -
Apply configured expiry to computed values in Redisson caches
Details
Values populated into a Redisson cache through
get(key, mappingFunction)— including read-throughget(key)andgetAll(keys)loading — were stored without the configuredexpireAfterWrite/expireAfterAccess, so those entries never expired. The configured expiry is now applied on these paths for both loading and non-loading Redisson caches.
8.2.1 (2026-06-10)
Feature
-
Resolve current user by ID instead of code
Details
The current user is now resolved by its User ID (primary key) rather than its user code. Fetching by primary key is more efficient, as it can be served directly from the L2 entity cache and avoids the query cache.
To support this, the Shiro subject principals now carry the User ID as a second principal, alongside the user code which remains the primary principal. All current-user lookups read this ID and load the user by primary key.
Migration: backward compatibility for sessions without a User ID principal has been removed. Any pre-existing user session is rejected and the user must log in again, after which the session carries the required ID principal. This only affects deployments with sessions that persist across server restarts.
Fix
-
Fix onClick context data for line, area, scatter, radar, funnel, and gauge charts
Details
Line, area, scatter, radar, funnel, and gauge charts were not passing the clicked record’s data to onClick actions, so the action context was always empty. Each chart type now correctly passes the original record as context when clicked, consistent with bar and pie charts. The funnel chart type is also added to the supported chart types documentation.
-
Fix reload-dotted view param support in grid view
Details
Grid view now bypasses the data cache when navigating back from a form view with the reload-dotted action param set, ensuring the grid data is refreshed on the switch from form to grid even when no field has changed.
-
Fix silent failure when removing an entity referenced through a bidirectional @OneToOne
Details
Removing an entity could silently succeed without issuing any
DELETESQL when the target entity was on the inverse side of a bidirectional@OneToOnewhose owning side declaredcascade = {PERSIST, MERGE}.Two Hibernate behaviors combined to cause the issue :
-
The inverse
@OneToOneis eagerly loaded despiteFetchType.LAZY(Hibernate cannot build a proxy without a foreign-key column), soEntityManager#findpulls the owning-side entity into the persistence context. -
During flush, the owning-side entity cascades a persist event back onto the deleted target via
cascade = PERSIST. Per JPA spec, persisting a removed entity returns it to the managed state, so it cancels the deletion before any SQL is emitted.
JpaRepository#removenow detaches the loaded inverse@OneToOnevalue when its back-reference is required (and therefore cannot be nulled) before delegating toJPA.remove, breaking the cascade graph that resurrected the entity. The actualDELETEreaches the database, and any foreign-key constraint is enforced normally. When the back-reference is optional, it is nulled as before so the referencing entity is preserved. -
-
Preserve existing
createdOn/createdByvalues on insertDetails
On insert,
AuditListeneralways overwrote thecreatedOnandcreatedByfields. These fields are now populated only when they are not already set, preserving any values explicitly provided on the entity. This restores the behavior from before v8, prior to the migration from a Hibernate interceptor to an event listener. -
Fix Caffeine L2 cache expiration configuration
Details
In
application.confCaffeine L2 cache configuration, durations without a unit are interpreted as milliseconds. The intended 1 hour expiration was 3.6 seconds instead.Expiration is now set to 10 minutes:
policy.eager-expiration.after-write = 10mRemoved
default-update-timestamps-regionexpiration as per Hibernate recommendation: https://docs.hibernate.org/stable/orm/userguide/html_single/#caching-query-regionAdded dedicated regions for frequently-read, rarely-changed User/Group/Permission/Role, with access-based expiration (
after-access = 24h) so actively-used entries stay cached while idle ones are evicted.Roleis nowcacheable="true", consistent with User/Group/Permission. -
Fix
updatedOn/updatedByaudit fields not being filled on updateDetails
The
updatedOnandupdatedByaudit fields were not populated when updating records of entities mapped with@DynamicUpdate, leaving them empty in the database. They are now correctly filled on every update. -
Allow CLI
database updateto load all modules, not only installed onesDetails
database update(without -m) previously updated only already-installed modules, silently skipping newly added ones. Now, all modules are loaded regardless of their installation status. -
Fix phone widget paste truncating last digit when country prefix is set
Details
When a country was pre-filled on a phone field (e.g. France, +33), pasting a national-format number such as 0608691275 caused the last digit to be dropped. The trunk prefix (leading 0) was counted against the country’s digit mask, leaving only 9 of the 10 significant digits.
Paste events are now intercepted and the pasted text is parsed with libphonenumber-js in the context of the selected country. If a valid number is recognized, it is converted to E.164 format before being handed to the input, bypassing the mask conflict. Pasting a full international number for a different country (e.g. +447700900123 into a French field) also correctly switches the country flag.
-
Load data init only on non-installed modules
Details
database updatere-imported thedata-initfiles of every installed module on each run. Data init (and demo data) are now loaded only for modules that are not yet installed. -
Fix broken drag and drop in dashboards
-
Fix context passing for dashlet scope
Details
Form view context should be merged with action-view context of dashlet for domain action, relative view fetching and search of dashlet. For dashlet actions (such as toolbar/menubar actions), form view context should pass _parent and action-view context of dashlet will be passed in top level domain context.
8.2.0 (2026-05-20)
Feature
-
Add zoomThreshold config option to charts
Details
A new
zoomThresholdper-chart config option controls data zoom slider behavior. Defaults to50.-
> 0— zoom slider enabled only when the number of data points exceeds the value; initial view shows the first N items. -
= 0— zoom slider always shown, fully zoomed out (all data visible); useful when you want precise zoom controls regardless of series size. -
< 0— zoom disabled; no zoom controls are shown.
<!-- Enable zoom slider only when more than 20 data points are present --> <config name="zoomThreshold" value="20" /> <!-- Always show zoom controls, full data visible --> <config name="zoomThreshold" value="0" /> <!-- Disable zoom controls entirely --> <config name="zoomThreshold" value="-1" /> -
-
Set number input text alignment to right in editable grid
Details
Number values are now right-aligned in editable grid cells. Increment/decrement controls, which remain always visible in editable grid cells while normal form fields keep their existing hover behavior.
-
Allow extending the implements attribute when overriding an entity
Details
When an entity is overridden in another module, the
implementsattribute is now merged with the parent entity’s value. New interfaces declared in the override are added to the existing list.Note: this is additive only — interfaces declared on the parent entity cannot be removed by an override.
-
Add custom model support for all view types
Details
Custom models (based on
MetaJsonRecord) can now be used across all view types: kanban, calendar, tree, gantt, cards, grid, and form.Custom JSON fields can be used in view-level attributes such as
columnBy,sequenceBy,eventStart,eventStop,colorBy,taskStart,taskParent,parent, etc. using dotted notation (e.g.attrs.status).Tree view nodes now support the
x-json-modelattribute to filter records by their custom model. Cards and kanban views also supportx-json-modelin the XML schema. -
Add jsonModel support for custom model views
Details
Add jsonModel field to MetaView and MetaViewCustom to properly resolve custom model views across relation widgets and view metadata APIs.
Fixes #1650
-
Upgrade GraalJS to 25.0.3 and optimize JavaScript integration
Details
-
Upgrade GraalJS from 24.2.2 to 25.0.3
-
Add shared engine and source caching for improved performance
-
Implement AutoCloseable on JavaScriptScriptHelper for proper context cleanup
-
Replace allowAllAccess(true) with explicit permissions for better security
-
Set ECMAScript version to 2024 (stable release)
-
-
Add Load more option for tree view child nodes
Details
Tree view child nodes are now loaded in pages using the default page size instead of all at once. When more children exist, a "Load more" row appears at the end of the list. Previously loaded children are cached so re-expanding a node does not trigger additional server calls. Refresh and sort clear the cache.
-
Make CodeEditor lighter, more usable and add auto-size / lite modes
Details
The CodeEditor widget was too heavy for typical form usage. It has been streamlined to be simpler, more usable, and more compact: minimap, sticky scroll, auto-completion and suggestions are disabled, scroll decoration shadow is removed, and consistent top/bottom padding is applied.
Default
heighthas been lowered from 400px to 360px.New features:
-
Auto-size mode via
x-auto-size="true": editor grows with content up to the specified height (defaults to 360px, min 36px). Vertical scrollbar is hidden while content fits and shown only when overflowing. -
Lite mode via
x-lite="true": minimal editor with no line numbers, folding, hover, suggestions, context menu or current-line highlight — suitable for small inline fields. -
placeholderattribute is now forwarded to the editor.
-
-
Add Export as PNG to chart dashlet menu
Details
Charts can now be exported as a PNG image from the dashlet action menu.
Note: the existing export action has been renamed from "Export" to "Export as CSV" for clarity.
-
Add maxSeries config option to charts to prevent excessive memory consumption
Details
Chart rendering for large datasets now limits excessive series output to prevent high memory consumption. The new
maxSeriesper-chart config keeps only the top N series by total value. Pie, donut, radar, line, area, and scatter charts aggregate the remaining series into an "Others" bucket; bar and horizontal bar charts drop tail series to preserve scale accuracy. Defaults to200; set to0to disable the limit.<!-- Show only the top 20 products --> <config name="maxSeries" value="20" /> -
Add cascade handling for json custom field relations
Details
Implement cascade persist, merge, and delete for custom field relationships stored as JSON (MetaJsonField). Cascade decisions are driven by a central policy: json-one-to-many to MetaJsonRecord is owned (persist/merge/remove + orphan removal), everything else is persist/merge only with no target deletion.
-
Add active session management for users
Details
Users can now view and revoke their active sessions from the user form. Each session displays the browser and OS, the client IP address, the sign-in time, and the last activity time. The current session is highlighted and cannot be revoked.
Admins can view and revoke sessions for any user. Non-admin users can only manage their own sessions.
-
Add isDelegated option for custom models
Details
Add isDelegated boolean field to MetaJsonModel to allow custom models to use manually-defined views instead of auto-generated ones.
-
Add UUID field type support for domain entities
Details
Added support for UUID field type in domain entities to store uuid values more efficiently. Also added a common UuidUtils to generate UUID v4 and v7.
Change
-
Change password reset token max-age unit from hours to minutes
Details
The
application.reset-password.max-ageconfiguration property now accepts a value in minutes instead of hours. The default has also been reduced from 24 hours to 30 minutes. If you have customized this setting, update your configuration accordingly. -
Change encrypted fields hashing algorithm
Details
The legacy
$AES$encryptor usedPBKDF2WithHmacSHA1, which is no longer considered secure for password-based key derivation. Encrypted fields now usePBKDF2WithHmacSHA256with 600,000 iterations. The ciphertext format is now self-describing: the iteration count, salt size, and IV size are embedded directly in the payload header. Decryption is therefore independent of current defaults, and future parameter changes never break existing stored values.The application transparently decrypts legacy
$AES$ciphertext alongside the new formats, so existing encrypted values continue to be readable without any immediate migration.To migrate existing
$AES$values to the new format, run the database encryption CLI tool. Noencryption.old-passwordorencryption.old-algorithmsettings are required when coming from the legacy encryptor.The new PBKDF2WithHmacSHA256 algorithm is significantly more CPU-intensive than the legacy
PBKDF2WithHmacSHA1. Each encrypted field uses a distinct random salt, so a separate AES key must be derived per field, and that derivation dominates the processing time.To keep throughput acceptable on large datasets, database encryption CLI tool uses default 100,000 iterations during migration instead of the runtime default of 600,000. This can be overridden with the
--iterationflag :axelor database encrypt --iteration 200000. Values written during migration can be re-migrated to a higher count at any time thanks to the self-describing format. -
Improve related messages endpoint performance
Details
The messages endpoint loaded the full related entity just to read its id and class. It now uses a lightweight existence check followed by a lazy reference (proxy), avoiding the full row fetch.
-
Change Query Selector values() row shape for many-to-one fields
Details
As a side-effect of dropping the redundant full-entity fetch for many-to-one fields in
Selector, the raw row shape returned bySelector.values(int, int)has changed for anyselect()that includes an m2o field.Before, a single slot in each row held the fully materialized referenced entity (e.g. a
Titleinstance), followed by three scalar slots forid,versionand the name field. Now, only the three scalars are emitted — the full-entity slot is gone.For example, with
select("firstName", "title", "title.code")the row layout is now:[ id, version, firstName, title.id, title.version, title.name, title.code ]
whereas previously it was:
[ id, version, firstName, <Title entity>, title.id, title.version, title.name, title.code ]
Callers that iterate
values()by index and expected the m2o entity at a specific position must be updated to read the three scalars instead. Callers that only useSelector.fetch(int, int)are unaffected — the output map shape is unchanged ({id,$version,nameField} compact map under the reference key). -
Rework user password update flow with identity verification
Details
The password update flow now requires identity verification before allowing changes. A new "Change Password" button replaces the inline password fields on the user form.
The
passwordfield onUseris no longer required — external provider users (SSO, OAuth) no longer receive a random UUID password.Non-admin users are now blocked from modifying restricted fields (
code,group,roles,permissions,password, etc.) via the User CRUD endpoint.SQL migration script:
ALTER TABLE auth_user ALTER COLUMN password DROP NOT NULL; -
Improve message followers endpoints performance
Details
The message followers endpoints loaded the full target entity just to read its id and class. They now use a lightweight existence check followed by a lazy reference (proxy), avoiding the full row fetch.
-
Exclude dotted collection paths from Query Selector
Details
Query.Selectorno longer includes dotted field paths that walk through or resolve to a collection (@OneToMany,@ManyToMany). These paths were silently accepted before and produced a SELECT column with an implicit join, which was rarely the intended behavior and often led to ambiguous or duplicated results.The following inputs to
select(...)are now silently dropped:-
paths where any intermediate segment is a collection, e.g.
addresses.streetorcontact.addresses.street -
paths whose leaf is itself a collection reached through a dotted expression, e.g.
contact.addresses
Single-segment collection fields like
select("addresses")are unaffected — they keep being resolved and returned as a list of compact maps in the output.If your code relied on the previous behavior to flatten a sub-field of a collection into the row, switch to either selecting the collection itself (
select("addresses")) and reading the sub-field from the nested compact maps, or issuing a dedicated query on the target entity. -
-
Remove MySQL and Oracle database support
Details
Both databases had only partial support that was never actively used or maintained. Removing them to reduce complexity and focus on PostgreSQL as the primary supported database.
-
Speed up embedded Tomcat startup with jar scan filtering
Details
Embedded Tomcat was scanning every jar on the classpath for annotations, TLDs, and web fragments. Configure StandardJarScanFilter to skip all jars by default. Add
--tld-scan-jars/--pluggability-scan-jarsCLI options to configure TLD scanning (JSP taglibs) and Servlet 3.0 pluggability scanning (web-fragment.xml, @WebServlet / @WebFilter / @WebListener) -
Upgrade backend dependencies
Details
Here is the list of backend dependencies upgraded :
-
Upgrade Jackson from 2.21.0 to 2.21.3
-
Upgrade Jsoup from 1.22.1 to 1.22.2
-
Upgrade Swagger from 2.2.43 to 2.2.50
-
Upgrade ByteBuddy from 1.18.5 to 1.18.8
-
Upgrade Birt from 4.22.0 to 4.23.0
-
Upgrade Snakeyaml from 2.5 to 2.6
-
Upgrade Ldaptive from 2.4.2 to 2.5.0
-
Upgrade Hibernate from 6.6.42 to 6.6.50
-
Upgrade Redisson from 3.52.0 to 4.3.1
-
Upgrade Junit from 5.14.3 to 6.0.3
-
Upgrade Groovy from 4.0.30 to 4.0.32
-
Upgrade Tomcat from 10.1.52 to 10.1.55
-
Upgrade Slf4j from 2.0.17 to 2.0.18
-
Upgrade Undertow from 2.3.23 to 2.3.24
-
Upgrade Gradle from 8.14.3 to 8.14.5
-
Upgrade PostgreSQL JDBC from 42.7.10 to 42.7.11
-
Upgrade Caffeine from 3.2.3 to 3.2.4
-
Upgrade Resteasy from 6.2.15 to 6.2.16
-
-
Improve chart rendering for large datasets
Details
Chart rendering has been improved to handle large datasets more reliably.
-
A loading spinner is shown while chart data is being fetched.
-
Axis labels are truncated when space is limited and a tooltip reveals the full label on hover.
-
Large mode and LTTB downsampling are applied automatically on scatter and line charts with very large datasets to keep rendering fast.
-
-
Improve user password requirements
Details
The password policy system has been reworked to provide more useful, secure, and customizable built-in policies.
Default behavior changes:
-
Minimum password length increased from 4 to 8 characters (mandatory, cannot be disabled).
-
New password must differ from the current one (mandatory, cannot be disabled).
-
Passwords must not contain the user login code (case-insensitive substring check).
New opt-in policies — each can be enabled with a single configuration property (
user.password.<policy>.enabled = true):-
digits— requires a minimum number of digit characters. -
lowerCase— requires a minimum number of lowercase characters. -
upperCase— requires a minimum number of uppercase characters. -
specialChars— requires a minimum number of special (non-alphanumeric) characters. -
pattern— requires the password to match a custom regular expression. -
score— requires a minimum strength score computed (0–4 scale).
Breaking changes:
-
The
user.password.patternproperty has been removed. Use the newpatternpolicy (user.password.pattern.enabled = true/user.password.pattern.value = <regex>) or combine the new built-in policies to achieve equivalent constraints. -
The
user.password.pattern-titleproperty has been removed.
-
-
Improve RestService download performance
Details
The download endpoint loaded the full entity to read a single field, which triggered an unnecessary fetch of the entire row including its associations. The field is now retrieved with a targeted projection query, reducing latency and avoiding spikes on entities with heavy relations. The permission check has also been consolidated.
-
Move CacheBuilder#weakValues/weakKeys to CaffeineCacheBuilder
Fix
-
Avoid shutdown hook deadlock during Tomcat startup
Details
The shutdown hook is now registered after Tomcat has fully started instead of during initialization, preventing a potential deadlock if shutdown is triggered while startup is still in progress.
-
Fix NPE from Mapper.toBean() with computed fields
Details
When mapping values to a bean, computed fields often depend on other field values. By populating non-virtual fields first, we ensure that dependencies are available when virtual fields are processed.
-
Fix reload expanded node on tree grid refresh
-
Fix encrypted fields migration
Details
Fix several issues during encrypted fields migration :
-
non-deterministic pagination issue that preventing already-migrated records from being re-processed at shifted offsets.
-
Handle inheritance strategies for entities : skip inherited fields for
SINGLE_TABLEand `JOINE`D so they are migrated by the parent class.
Also few improvements has been done :
-
Use JDBC batch statements instead of individual JPQL UPDATE queries : select decrypted values via JPA (so the old encryptor is applied automatically), then re-encrypt and persist via
PreparedStatement.addBatch()/executeBatch()for better performance and reduced DB load -
Add validation of migration settings (required password, known algorithms, warn on no-op rotations) before starting
-
Infer missing old encryption settings from current values when rotating : only need to configure what actually changed. Ex : no need to set
old-passwordis we rotate only the algorithm.
-
-
Fix tree node orderBy attribute not being applied
Details
orderByattribute defined on tree node was not being applied during the search request of the node. -
Fix itemSpan not applied to custom JSON fields in form view
Details
itemSpandefined on parent container was not being applied to custom JSON fields in form view. -
Fix translatable text/html widgets ignoring translation in readonly
Details
When a translation exists for the current locale, the text and html widgets now display the translated value in readonly mode, matching the behavior of the string widget. Previously the editable input was shown with the raw stored value instead.
-
Fix Tags widget readonly rendering in form and grid views
Details
When a Tags widget (
widget="Tags") was set to readonly, it rendered as a plain empty text input instead of displaying tag pills. The readonly Tags widget now renders tag pills directly using the same Overflow layout as the grid cell renderer, with proper overflow menu support. -
Fix copy and export errors not displayed to user
Details
When an exception is thrown from the copy or export methods, the error is now displayed in a dialog instead of being silently ignored.
-
Fix view-param limit support in tree view
Details
The view-param limit support in tree view was not being applied to the root node.
-
Fix scrollbar flash when downloading file
-
Fix translatable text/html widgets flag button position and visibility
Details
In the text widget, the translation flag button was not positioned correctly next to the input. In the html widget, the flag button was hidden entirely when a translation existed. Both widgets now show the flag button at the top-right of the field, consistent with the string widget behavior.
-
Avoid full m2o entity fetch in Query Selector
Details
When a many-to-one field like
titlewas included in aselect(), the generated JPQL contained both the bare join alias (_title) and itsid/version/nameFieldscalars. The bare alias caused Hibernate to materialize the entire referenced entity — every column of the target table — even though only the compact {id,$version,nameField} map was ever returned to callers.The full entity fetch is now dropped: only the three scalars are selected, which is all
fetch()actually consumes. -
Consolidate form validation errors by priority per field
Details
When saving a form with multiple constraint violations on the same field, all errors were shown (e.g. "Login is invalid" and "Login is too small"). Now only the highest-priority error per field is displayed, using this order: required > min/max > pattern > custom errors > invalid.
Also fix server-side validation errors using the raw field name (e.g. "code") instead of the field title (e.g. "Login").
-
Fix XSS vulnerability in download filename handling
Details
Use textContent instead of innerHTML when setting the download link text to prevent script execution via crafted filenames.
-
Fix NPE in JobController cron validation with no further valid firing times
-
Fix central logout and session timeout redirect when MFA is enabled
Details
MfaClient restores pending profile but overwrites its clientName. Now always restore pending client name when session is marked as fully authenticated by MFA authenticator.
-
Fix mail followers recipients request limit
Details
The mail followers recipient request limit wasn’t provided, allowing related service to be called with an unbounded limit. Cap the effective limit to
api.pagination.max-per-pageso that the email search respects the configured pagination maximum and useapi.pagination.default-per-pageas the default when no limit is specified in the request. -
Fix pass selected in custom collection field context
-
Fix ConcurrentModificationException in AuditTracker on entity delete
Details
AuditTracker.processDelete() now iterates over a snapshot of the deleted entities set, preventing ConcurrentModificationException when cascaded deletes add new entries during iteration.
-
Fix various cache issues
Details
Fix various issues with caches including incorrect log arguments, swallowed exceptions in eviction listeners, NPE in default
getAll()method, premature eviction of tenant caches in cluster environments, closing underlying tenant-specific caches when closing wrapper tenant-aware cache. -
Fix NPE in AuditProcessor when processing computed fields
-
Fix theme colors not applied to panel dashlet
Details
Refactor dashlet widget to use the Panel component from @axelor/ui so that theme color variables are correctly applied to the dashlet header and body. Also fix chart views not occupying the full height of the dashlet container.
-
Fix handle focus navigation in grid view search column
-
Fix gauge chart showing NaN% when category axis is formatted
Details
Gauge charts were reading the value from the category axis field (e.g. month), which gets formatted into a string (e.g. "May") by the chart data pipeline. Using a string as the gauge value caused ECharts to display NaN%. The gauge now correctly reads the value from the series key field instead.
-
Fix image widget binary field to use $upload
Details
Image widget on binary fields now uses the $upload mechanism via the upload endpoint instead of sending the file as a base64 data URL in the save payload.
-
Fix NPE in MetaFiles.upload() recovery block
Details
Add null check for temporary file copy to prevent masking of root persistence exceptions with NPEs during new file uploads.
-
Fix calendar drag-and-drop not saving when popover is open
Details
Close the event popover when a drag operation starts on the calendar view. Previously, if the template popover was open, dragging an event to another date would not trigger a save.
-
Fix OOM in OpenAPI scanner by limiting scan to Axelor modules
Details
OpenAPI scanner now limits the scan to Axelor modules only, reducing memory usage and preventing OutOfMemoryError. So resources in non-Axelor JARs no longer appear in the spec.
-
Fix date widget portal focus handling in editable grid
Details
Keep the date picker rendered in portal mode for grid layout stability, while preventing focus-trap and blur interactions from causing repeated blur loops or React update crashes when using month/year selectors.
-
Fix cache busting of JSP and HTML files
Details
Exclude .jsp, .html, and .htm files from static resource cache busting in NoCacheFilter. These files should not receive immutable cache headers as they are server-rendered or serve as application entry points.
Security
-
Upgrade Pac4j & Shiro dependencies
Details
Upgrade Pac4j from 6.3.1 to 6.5.1 and Shiro from 2.0.6 to 2.1.0 for security reasons
-
Fix RestService upload missing file content type validation
Details
Only the declared file type from the request data was validated against the whitelist/blacklist, allowing a malicious user to bypass validation by sending a file with an allowed extension but different content. Now the actual file content is inspected via Apache Tika for both attachment and non-attachment uploads.
-
Fix file upload content type validation to prevent extension spoofing
Details
MetaFiles.checkType(File) now pass null as the filename hint to Tika, ensuring detection relies solely on magic bytes. Previously, the filename was used as a hint and could influence the result, allowing a malicious file disguised with an allowed extension to bypass validation.