MudBlazor 9 renders mud-tabs-vertical with the tabbar flush against the panels
container, making the active tab indicator visually touch the panel content.
Add 16px padding-inline-start on the panels area (and -end for the reverse layout)
so the tabbar and panels have breathing room.
MudBlazor 9 defaults .mud-input-label-inputcontrol to line-height: 1.15rem, which is
smaller than a typical 16px character with descenders. Letters like g, p, q, y in the
floated label visually clipped under the input baseline. Bump to 1.4rem so descenders
have room without enlarging the overall control.
Long label text (e.g. Turkish translations) on MudDatePicker / MudTimePicker overflowed
the input and visually overlapped the calendar toggle icon on the right. Reserve 40px
on the right for the end-adornment icon so the label clips with ellipsis.
- MudMenu: switch ActivatorContent to MenuContext.ToggleAsync pattern (v9 breaking)
- MudForm: rename Validate to ValidateAsync (v9.1 obsoletion)
- MudInput: replace AutoGrow with Sizing for textarea
- DataGrid: add white-space:nowrap on header to prevent CJK characters stacking vertically
- BlockUI: shrink loading spinner inside mud-dialog
- Search field: switch Label to Placeholder so the text shows next to the magnifier icon
- Soften IsSandboxed XML doc as a best-effort marker
- Replace #XXXXX placeholders with #25399
- Set Scriban MemberFilter to allowlist public properties only,
blocking method/field access and reflection escape paths
- Update Razor and Scriban safe-runtime docs to match
- Add reflection-escape, method-invocation and nested-property
tests for Scriban
ITemplateRenderingEngine exposes a new IsSandboxed property so callers can
decide whether editing a template requires elevated trust.
- TemplateRenderingEngineBase provides a virtual default of false (secure-by-default)
- RazorTemplateRenderingEngine declares IsSandboxed=false (compiles to .NET assembly via Roslyn)
- ScribanTemplateRenderingEngine declares IsSandboxed=true (DSL without .NET interop)
- Razor integration docs and TextTemplateManagement docs document the implications
- Migration guide for ABP 10.4 documents the new abstraction member
Saas tenant create modal (and other mud CRUD pages) duplicated rows when
the user double-clicked Save: the second click hit AppService.CreateAsync
before the first response had returned.
Add IsCreating / IsUpdating gates to AbpMudCrudPageBase so concurrent
calls return immediately. Wrap the body in try/finally and trigger
StateHasChanged so any UI bindings to these flags reflect the busy state.
InMemoryDynamicBackgroundWorker indirectly implements ISingletonDependency
via IBackgroundWorker, so conventional registration tries to register it
as a service. Its constructor takes a string workerName parameter that
the DI container cannot resolve, which crashes any host that runs
ServiceCollection validation (e.g. ASP.NET Core in Development, where
WebApplicationBuilder.Build() enables ValidateOnBuild). The dynamic worker
is created on demand by DefaultDynamicBackgroundWorkerManager and must
not be auto-registered, so mark it with [DisableConventionalRegistration].
- Look up the existing aria-describedby attribute with OrdinalIgnoreCase to match the casing rules used by HTML and TagHelperAttributeList
- Tokenize the existing value on all ASCII whitespace (space, tab, newline, carriage return, form feed) instead of just the literal space
- Cover the whitespace-separated case with a new test
Cover the new aria-describedby behaviour for <abp-input>:
- form-text rendered as <div>
- aria-describedby reaches the final HTML
- no-id case skips the InfoText id and aria-describedby
- caller-supplied aria-describedby is preserved (append + dedupe)
- [InputInfoText] attribute path produces a single aria-describedby
- Add TagHelperOutputExtensions.AppendAriaDescribedby helper that preserves caller-supplied tokens (space-separated id list) and dedupes
- Replace SetAttribute calls in AddInfoTextId/GetInfoAsHtml of abp-input/abp-select with the helper
- Cover the consumer-provided aria-describedby case with a new test
- Stop using the localized text as the aria-describedby value in AddInfoTextId; reference the actual id directly
- Skip rendering the InfoText id and aria-describedby when the input/select has no id (or an empty one) so the form never renders a non-unique "InfoText" id
- Cover the no-id case with a new test
- Move inputTag.Render after GetInfoAsHtml so aria-describedby reaches the final HTML
- Replace Attributes.Add with Attributes.SetAttribute for aria-describedby to avoid duplicates when [InputInfoText] and info="..." are both present
- Apply the same fixes to AbpInputTagHelperService for consistency
- Cover the [InputInfoText] attribute path with an additional test
- Replace <small> with <div> to match abp-input and the Bootstrap 5 form-text guidance
- Add aria-describedby on the select so screen readers announce the description
Resolve `WebAssemblyStyleFiles` and `WebAssemblyScriptFiles` through `IComponentBundleUrlBuilder` so they receive the same `PathBase` prefixing as the prerender bundle files.
- Extract provider name mapping into EfCoreDatabaseProviderHelper
- Match provider names by Contains instead of exact switch, so newer assembly names (e.g. MySql.EntityFrameworkCore) are recognized without code changes
- Add unit tests covering real provider assemblies and string fallbacks
Add an optional ExclusionFilters parameter (default: ExclusionFilters.Sensitive) to ReplaceEmbeddedByPhysical and forward it to the PhysicalFileProvider constructor when creating PhysicalVirtualFileSetInfo. This lets callers control which files are excluded when replacing embedded virtual file sets with physical directories.
Add an optional ExclusionFilters parameter (default: ExclusionFilters.Sensitive) to ReplaceEmbeddedByPhysical and forward it to the PhysicalFileProvider constructor when creating PhysicalVirtualFileSetInfo. This lets callers control which files are excluded when replacing embedded virtual file sets with physical directories.
Replace linear scans with Dictionary/HashSet lookups in both
RequirePermissionsSimpleBatchStateChecker and
RequireFeaturesSimpleBatchStateChecker for consistency.
When IsEnabledAsync(TState[]) evaluates non-batch checkers, the original
implementation called InternalIsEnabledAsync for each state, which created
a new DI scope every time. In real-world scenarios with thousands of
permissions (e.g. 4050 permissions each with RequireFeaturesSimpleStateChecker),
this caused N scope creations and N redundant Redis queries, resulting in
~3.5s latency.
This change shares a single DI scope across all non-batch checker evaluations
in the batch path by extracting EvaluateCheckersAsync and calling it directly
with the shared scope. Each state still gets an isolated ITransientCachedServiceProvider
to prevent transient service leakage across states, while scoped services
(e.g. IFeatureChecker) are naturally shared within the scope, enabling cache reuse.
The single-state path (IsEnabledAsync(TState)) remains completely unchanged.
Use JsonSerializer for non-primitive types in AbpLoggerExtensions.LogData
to output meaningful JSON instead of type names like List`1[Dictionary`2[...]]
Prevents 'Synchronous operations are disallowed' error when rendering
large MVC views (e.g. Account settings page with multiple tabs in
non-English locales).