Release date: Not yet released
The utilization of Newtonsoft.Json has been discontinued in both YesSql and OrchardCore. Instead, we have transitioned to utilize System.Text.Json
due to its enhanced performance capabilities. To ensure compatibility with System.Text.Json
during the serialization or deserialization of objects, the following steps need to be undertaken:
- If your custom
Document
includes a collection with a getter-only property, it is imperative to incorporate a setter or utilize theinit
modifier to facilitate the assignment of values bySystem.Text.Json
. For instance:
public class MediaProfilesDocument : Document
{
public Dictionary<string, MediaProfile> MediaProfiles { get; } = new(StringComparer.OrdinalIgnoreCase);
}
Should be changed to the following instead:
public class MediaProfilesDocument : Document
{
public Dictionary<string, MediaProfile> MediaProfiles { get; init; } = new(StringComparer.OrdinalIgnoreCase);
}
- If you are using a custom deployment steps, change how you register it by using the new
AddDeployment<>
extension. This extension adds a new service that is required for proper serialization. For instance, instead of registering your deployment step like this:
services.AddTransient<IDeploymentSource, AdminMenuDeploymentSource>();
services.AddSingleton<IDeploymentStepFactory>(new DeploymentStepFactory<AdminMenuDeploymentStep>());
services.AddScoped<IDisplayDriver<DeploymentStep>, AdminMenuDeploymentStepDriver>();
change it to the following:
services.AddDeployment<AdminMenuDeploymentSource, AdminMenuDeploymentStep, AdminMenuDeploymentStepDriver>();
- If you are using a custom AdminMenu node, change how you register it by using the new
AddAdminNode<>
extension. This extension adds a new service that is required for proper serialization. For instance, instead of registering your custom admin menu nodep like this:
services.AddSingleton<IAdminNodeProviderFactory>(new AdminNodeProviderFactory<PlaceholderAdminNode>());
services.AddScoped<IAdminNodeNavigationBuilder, PlaceholderAdminNodeNavigationBuilder>();
services.AddScoped<IDisplayDriver<MenuItem>, PlaceholderAdminNodeDriver>();
change it to the following:
services.AddAdminNode<PlaceholderAdminNode, PlaceholderAdminNodeNavigationBuilder, PlaceholderAdminNodeDriver>();
- Any serializable object that contains a polymorphic property (a base type that can contain sub-classes instances) needs to register all possible sub-classes this way:
services.AddJsonDerivedTypeInfo<UrlCondition, Condition>();
In particular, any type introduced in custom modules inheriting from MenuItem
, AdminNode
, Condition
, ConditionOperator
, Query
, SitemapType
will have to use this method.
Previously, .pdf
files were automatically indexed in the search providers (Elasticsearch, Lucene or Azure AI Search). Now, if you want to continue to index .PDF
file you'll need to enable the OrchardCore.Media.Indexing.Pdf
feature.
Additionally, if you need to enable indexing for text file with .txt
, .md
extensions, you will need the OrchardCore.Media.Indexing.Text
feature.
If you need to enable indexing for other extensions like (.docx
, or .pptx
), you will need the OrchardCore.Media.Indexing.OpenXML
feature.
In the past, we utilized the injection of ISmsProvider
for sending SMS messages. However, in this release, it is now necessary to inject ISmsService
instead.
Additionally, Twilio
provider is no longer enabled by default. If you want to use Twilio SMS provider, you must enable the provider by visiting the email settings Configuration
> Settings
> Email
and see the Twilio
tab.
Introducing a new "Azure AI Search" module, designed to empower you in the administration of Azure AI Search indices. When enabled with the "Search" module, it facilitates frontend full-text search capabilities through Azure AI Search. For more info read the Azure AI Search docs.
Added new extensions to make registering custom deployment step easier:
services.AddDeployment<TSource, TStep>()
.services.AddDeployment<TSource, TStep, TDisplayDriver>()
.services.AddDeploymentWithoutSource<TStep, TDisplayDriver>()
.
The method Task TriggerEventAsync(string name, IDictionary<string, object> input = null, string correlationId = null, bool isExclusive = false, bool isAlwaysCorrelated = false)
was changed to return Task<IEnumerable<WorkflowExecutionContext>>
instead.
When identifying content types for GraphQL exposure, we identify those without a stereotype to provide you with control over the behavior of stereotyped content types. A new option, DiscoverableSterotypes
, has been introduced in GraphQLContentOptions
. This allows you to specify stereotypes that should be discoverable by default.
For instance, if you have several content types stereotyped as ExampleStereotype
, you can make them discoverable by incorporating the following code into the startup class:
services.Configure<GraphQLContentOptions>(options =>
{
options.DiscoverableSterotypes.Add("ExampleStereotype");
});
The OrchardCore.Email
module has undergone a refactoring process with no breaking changes. However, there are compile-time warnings that are recommended to be addressed. Here is a summary of the changes:
- Previously, we used the injection of
ISmtpService
for sending email messages. In this release, it is now necessary to injectIEmailService
instead. - The
SMTP
related services are now part of a new module namedOrchardCore.Email.Smtp
. To use the SMTP provider for sending emails, enable theOrchardCore.Email.Smtp
feature. - If you were using the
OrchardCore_Email
configuration key to set up the SMTP provider for all tenants, please update the configuration key toOrchardCore_Email_Smtp
. TheOrchardCore_Email
key continues to work but will be deprecated in a future release. - A new email provider was added to allow you to send email using Azure Communication Services Email. Click here to read more about the ACS module.
The admin menu has undergone performance enhancements, and new helpers have been added. When incorporating INavigationProvider
in your project, you can now utilize NavigationHelper.IsAdminMenu(name)
instead of the previous approach using string.Equals(name, "admin", StringComparison.OrdinalIgnoreCase)
. Moreover, when passing route values to an action, it is advised to store them in a constant variable. An illustrative example is provided below.
public class AdminMenu : INavigationProvider
{
private static readonly RouteValueDictionary _routeValues = new()
{
{ "area", "OrchardCore.Settings" },
{ "groupId", AdminSiteSettingsDisplayDriver.GroupId },
};
protected readonly IStringLocalizer S;
public AdminMenu(IStringLocalizer<AdminMenu> stringLocalizer)
{
S = stringLocalizer;
}
public Task BuildNavigationAsync(string name, NavigationBuilder builder)
{
if (!NavigationHelper.IsAdminMenu(name))
{
return Task.CompletedTask;
}
builder
.Add(S["Configuration"], configuration => configuration
.Add(S["Settings"], settings => settings
.Add(S["Admin"], S["Admin"].PrefixPosition(), admin => admin
.AddClass("admin")
.Id("admin")
.Action("Index", "Admin", _routeValues)
.Permission(PermissionsAdminSettings.ManageAdminSettings)
.LocalNav()
)
)
);
return Task.CompletedTask;
}
}
The [Admin]
attribute now has optional parameters for a custom route template and route name. It works just like the [Route(template, name)]
attribute, except it prepends the configured admin prefix. You can apply it to the controller or the action; if both are specified then the action's template takes precedence. The route name can contain {area}
, {controller}
, and {action}
, which are substituted during mapping so the names can be unique for each action. This means you don't have to define these admin routes in your module's Startup
class anymore, but that option is still available and supported. Take a look at this example:
[Admin("Person/{action}/{id?}", "Person{action}")]
public class PersonController : Controller
{
[Admin("Person", "Person")]
public IActionResult Index() { ... }
public IActionResult Create() { ... }
public IActionResult Edit(string id) { ... }
}
In this example, (if the admin prefix remains the default "Admin") you can reach the Index action at ~/Admin/Person
(or by the route name Person
), because its own action-level attribute took precedence. You can reach Create at ~/Admin/Person/Create
(route name PersonCreate
) and Edit for the person whose identifier string is "john-doe" at ~/Admin/Person/john-doe
(route name PersonEdit
).
Added a new User Localization feature that allows to be able to configure the culture per user from the admin UI.