diff --git a/Directory.Packages.props b/Directory.Packages.props index fa951f8d7d..6a900d2ab6 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -181,7 +181,7 @@ - + diff --git a/docs/en/release-info/release-notes.md b/docs/en/release-info/release-notes.md index bf335fa7a2..08d0bcde31 100644 --- a/docs/en/release-info/release-notes.md +++ b/docs/en/release-info/release-notes.md @@ -14,8 +14,23 @@ Also see the following notes about ABP releases: * [ABP Studio release notes](../studio/release-notes.md) * [Change logs for ABP pro packages](https://abp.io/pro-releases) +## 10.1 (2026-01-06) + +> This is currently a RC (release-candidate) and you can see the detailed **[blog post / announcement](https://abp.io/community/announcements/announcing-abp-10-1-release-candidate-cyqui19d)** for the v10.1 release. + +* Resource-Based Authorization +* Introducing the [TickerQ Background Worker Provider](../framework/infrastructure/background-workers/tickerq.md) +* Angular UI: Version Upgrade to **v21** +* [File Management Module](../modules/file-management.md): Public File Sharing Support +* [Payment Module](../modules/payment.md): Public Page Implementation for Blazor & Angular UIs +* [AI Management Module](../modules/ai-management/index.md) for Blazor & Angular UIs +* [Identity PRO Module](../modules/identity-pro.md): Password History Support +* [Account PRO Module](../modules/account-pro.md): Introducing WebAuthn Passkeys + ## 10.0 (2025-11-18) +> **Note**: ABP has upgraded to .NET 10.0, so if you plan to use ABP 10.0, you’ll need to migrate your solutions to .NET 10.0. You can refer to the [Migrate from ASP.NET Core 9.0 to 10.0](https://learn.microsoft.com/en-us/aspnet/core/migration/90-to-100) documentation for guidance. However, ABP’s NuGet packages are compatible with both .NET 9 and .NET 10, allowing developers to continue using .NET 9 while still enjoying the latest features and improvements of the ABP Framework without upgrading their SDK. + See the detailed **[blog post / announcement](https://abp.io/community/announcements/abp.io-platform-10.0-final-has-been-released-spknn925)** for the v10.0 release. * Upgraded to .NET 10.0 diff --git a/docs/en/release-info/road-map.md b/docs/en/release-info/road-map.md index 0df94ec157..70a9b392c4 100644 --- a/docs/en/release-info/road-map.md +++ b/docs/en/release-info/road-map.md @@ -1,7 +1,7 @@ ```json //[doc-seo] { - "Description": "Explore the ABP Platform Road Map for insights on upcoming features, release schedules, and improvements in version 9.1, launching January 2025." + "Description": "Explore the ABP Platform Road Map for insights on upcoming features, release schedules, and improvements in version 10.1, launching January 2026." } ``` @@ -11,33 +11,35 @@ This document provides a road map, release schedule, and planned features for th ## Next Versions -### v10.1 +### v10.2 -The next version will be 10.1 and planned to release the stable 10.1 version in January 2026. We will be mostly working on the following topics: +The next version will be 10.2 and planned to release the stable 10.2 version in April 2026. We will be mostly working on the following topics: * Framework - * OpenTelemetry Protocol Support for 3rd-party Integrations - * Resource Based Authorization Integration + * Resource-Based Authorization Improvements + * Handle datetime/timezon in `AbpExtensibleDataGrid` Component * Upgrading 3rd-party Dependencies * Enhancements in the Core Points * ABP Suite - * Define Navigation Properties Without Target String Property Dependency + * Creating enums on-the-fly (without needing to create manually on the code side) + * Improvements on the generated codes for nullability * Improvements on Master-Detail Page Desing (making it more compact) * Improvements One-To-Many Scenarios * File Upload Modal Enhancements * ABP Studio * Allow to Directly Create New Solutions with ABP's RC (Release Candidate) Versions + * Integrate AI Management Module with all solution templates and UIs * Automate More Details on New Service Creation for a Microservice Solution * Allow to Download ABP Samples from ABP Studio - * Task Panel Enhancements (and Documentation) + * Task Panel Documentation * Support Multiple Concurrent Kubernetes Deployment/Integration Scenarios * Improve the Module Installation Experience / Installation Guides * Application Modules - * Payment Module: Public Page Implementation (for Blazor & Angular UIs) - * AI Management Module: UI Implementation for Blazor & Angular UIs + * AI Management: MCP & RAG Supports + * File Management: Using Resource-Based Permission (on file-sharing and more...) * CMS Kit: Enhancements for Some Features (Rating, Dynamic Widgets, FAQ and more...) * UI/UX Improvements on Existing Application Modules diff --git a/docs/en/studio/release-notes.md b/docs/en/studio/release-notes.md index d2eba88e0f..13e206127e 100644 --- a/docs/en/studio/release-notes.md +++ b/docs/en/studio/release-notes.md @@ -9,7 +9,14 @@ This document contains **brief release notes** for each ABP Studio release. Release notes only include **major features** and **visible enhancements**. Therefore, they don't include all the development done in the related version. -## 2.1.3 (2025-12-15) Latest +## 2.1.4 (2025-12-30) Latest + +* Fixed books sample for blazor-webapp tiered solution. +* Fixed K8s cluster deployment issues for microservices. +* Fixed docker build problem on microservice template. +* Showed logs of the executed tasks. + +## 2.1.3 (2025-12-15) * Updated `createCommand` and CLI help for multi-tenancy. * Fixed `BookController` templating problem. diff --git a/framework/src/Volo.Abp.AspNetCore/Microsoft/Extensions/DependencyInjection/CookieAuthenticationOptionsExtensions.cs b/framework/src/Volo.Abp.AspNetCore/Microsoft/Extensions/DependencyInjection/CookieAuthenticationOptionsExtensions.cs index 7d6955f08b..85cc987b61 100644 --- a/framework/src/Volo.Abp.AspNetCore/Microsoft/Extensions/DependencyInjection/CookieAuthenticationOptionsExtensions.cs +++ b/framework/src/Volo.Abp.AspNetCore/Microsoft/Extensions/DependencyInjection/CookieAuthenticationOptionsExtensions.cs @@ -61,11 +61,13 @@ public static class CookieAuthenticationOptionsExtensions return; } + var clientId = principalContext.Properties.GetString("client_id"); + var clientSecret = principalContext.Properties.GetString("client_secret"); var response = await openIdConnectOptions.Backchannel.IntrospectTokenAsync(new TokenIntrospectionRequest { Address = introspectionEndpoint, - ClientId = openIdConnectOptions.ClientId!, - ClientSecret = openIdConnectOptions.ClientSecret, + ClientId = clientId ?? openIdConnectOptions.ClientId!, + ClientSecret = clientSecret ?? openIdConnectOptions.ClientSecret, Token = accessToken }); diff --git a/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/TimeZoneHelper.cs b/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/TimeZoneHelper.cs index 6101585878..23446069a9 100644 --- a/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/TimeZoneHelper.cs +++ b/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/TimeZoneHelper.cs @@ -7,14 +7,43 @@ namespace Volo.Abp.Timing; public static class TimeZoneHelper { + /// + /// Returns timezone list ordered by display name, enriched with UTC offset, filtering out invalid ids. + /// public static List GetTimezones(List timezones) { return timezones .OrderBy(x => x.Name) - .Select(x => new NameValue( $"{x.Name} ({GetTimezoneOffset(TZConvert.GetTimeZoneInfo(x.Name))})", x.Name)) + .Select(TryCreateNameValueWithOffset) + .OfType() .ToList(); } + /// + /// Builds a with the original timezone ID in Value and a display name that includes + /// the UTC offset in the Name property; returns null if the id is not found. + /// + public static NameValue? TryCreateNameValueWithOffset(NameValue timeZone) + { + try + { + var timeZoneInfo = TZConvert.GetTimeZoneInfo(timeZone.Name); + var name = $"{timeZone.Name} ({GetTimezoneOffset(timeZoneInfo)})"; + return new NameValue(name, timeZone.Name); + } + catch (Exception) + { + // Invalid or unknown timezone IDs are expected here (e.g. from user input or + // external sources). We intentionally swallow this exception and return null + // so callers (like GetTimezones) can filter out invalid entries. + } + + return null; + } + + /// + /// Formats the base UTC offset as "+hh:mm" or "-hh:mm" for display purposes. + /// public static string GetTimezoneOffset(TimeZoneInfo timeZoneInfo) { if (timeZoneInfo.BaseUtcOffset < TimeSpan.Zero) diff --git a/framework/test/Volo.Abp.Timing.Tests/Volo/Abp/Timing/TimeZoneHelper_Tests.cs b/framework/test/Volo.Abp.Timing.Tests/Volo/Abp/Timing/TimeZoneHelper_Tests.cs new file mode 100644 index 0000000000..54d8d37361 --- /dev/null +++ b/framework/test/Volo.Abp.Timing.Tests/Volo/Abp/Timing/TimeZoneHelper_Tests.cs @@ -0,0 +1,39 @@ +using System.Collections.Generic; +using Shouldly; +using TimeZoneConverter; +using Volo.Abp.Testing; +using Xunit; + +namespace Volo.Abp.Timing; + +public class TimeZoneHelper_Tests : AbpIntegratedTest +{ + [Fact] + public void GetTimezones_Should_Filter_Invalid_Timezones() + { + var validTimeZoneId = "UTC"; + var invalidTimeZoneId = "Invalid/Zone"; + + var timezones = new List + { + new(invalidTimeZoneId, invalidTimeZoneId), + new(validTimeZoneId, validTimeZoneId) + }; + + var result = TimeZoneHelper.GetTimezones(timezones); + + result.Count.ShouldBe(1); + + var expectedTimeZoneInfo = TZConvert.GetTimeZoneInfo(validTimeZoneId); + var expectedName = $"{validTimeZoneId} ({TimeZoneHelper.GetTimezoneOffset(expectedTimeZoneInfo)})"; + + result[0].Name.ShouldBe(expectedName); + result[0].Value.ShouldBe(validTimeZoneId); + } + + [Fact] + public void TryCreateNameValueWithOffset_Should_Return_Null_For_Invalid_Timezone() + { + TimeZoneHelper.TryCreateNameValueWithOffset(new NameValue("Invalid/Zone", "Invalid/Zone")).ShouldBeNull(); + } +} diff --git a/npm/ng-packs/packages/schematics/src/utils/methods.ts b/npm/ng-packs/packages/schematics/src/utils/methods.ts index d3fc935819..6d31903a78 100644 --- a/npm/ng-packs/packages/schematics/src/utils/methods.ts +++ b/npm/ng-packs/packages/schematics/src/utils/methods.ts @@ -19,4 +19,9 @@ export const getParamValueName = (paramName: string, descriptorName: string) => export function isDictionaryType(type?: string, typeSimple?: string): boolean { const haystacks = [type || '', typeSimple || '']; return haystacks.some(t => /(^|\b)(System\.Collections\.Generic\.)?(I)?Dictionary\s* /(^|\b)(System\.Collections\.Generic\.)?(I)?(List|Enumerable|Collection)\s* `${x}[]`).some(x => x === type); + // Check for array types like Volo.Abp.Content.IRemoteStreamContent[] + if (VOLO_REMOTE_STREAM_CONTENT.map(x => `${x}[]`).some(x => x === type)) { + return true; + } + + // Check for collection types like List, IEnumerable, ICollection, Collection, IList + // This matches any generic type from System.Collections.Generic that implements IEnumerable + if (isCollectionType(type)) { + const { generics } = extractGenerics(type); + if (generics.length > 0 && VOLO_REMOTE_STREAM_CONTENT.includes(generics[0])) { + return true; + } + } + + return false; } function getMethodNameFromAction(action: Action): string {