Skip to content

Latest commit

 

History

History
2254 lines (1760 loc) · 164 KB

2.0.0.md

File metadata and controls

2254 lines (1760 loc) · 164 KB

Orchard Core 2.0.0

Release date: September 9, 2024.

🎉 Orchard Core 2.0 Is Here – Elevate Your Projects to the Next Level! 🎉

We are thrilled to announce the second major release of Orchard Core! This release is packed with performance improvements, bug fixes, and a variety of enhancements that make it our best version yet. Whether you're building a new project or maintaining an existing one, Orchard Core 2.0 brings stability and power to your fingertips.

🚀 Why Upgrade to Orchard Core 2.0?

  • Enhanced Performance: Your sites and applications will run faster, smoother, and more efficiently.
  • Rock-Solid Stability: We've squashed numerous bugs, ensuring a more reliable and enjoyable development experience.
  • Seamless Development: By sticking to version control best practices, we've ensured no breaking changes in any patch or minor release, allowing developers to confidently create modules and libraries that will work across the entire 2.x series.

🎯 Module and Library Developers, Take Note!

Want to create a module or library for Orchard Core 2.0? We've made it easier than ever to build with confidence. Simply set your dependencies to [2.0, 3.0) to ensure full compatibility with future updates, without worrying about breaking changes.

Important Upgrade Instructions

Prior to making the leap to Orchard Core 2.0, it's crucial to follow a step-by-step upgrade process. Begin by upgrading your current version to 1.8, resolving any obsolete warnings encountered along the way, and guaranteeing that every site successfully operates on version 1.8.3. This approach ensures that all sites are seamlessly transitioned to 1.8. Subsequently, proceed with confidence to upgrade to 2.0. Prior to initiating this upgrade, thoroughly review the documented list of breaking changes provided below to facilitate a smooth and hassle-free transition.

Breaking Changes

Drop Newtonsoft.Json Support

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 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>();

Alternatively, you can simplify your code by using the newly added extensions to register custom conditions. For example,

services.AddRule<HomepageCondition, HomepageConditionEvaluator, HomepageConditionDisplayDriver>();
  • Any type introduced in custom modules inheriting from MenuItem, AdminNode, Condition, ConditionOperator, Query, SitemapType will have to register the class using the services.AddJsonDerivedTypeInfo<> method. For example,
services.AddJsonDerivedTypeInfo<SqlQuery, Query>();
  • The extension PopulateSettings<T>(model) was removed from PartFieldDefinition. If you are using this method in your code, you'll have to get the settings using the Settings object directly. For instance, if you have this code,
public override IDisplayResult Edit(ContentPartFieldDefinition partFieldDefinition)
{
   return Initialize<NumericFieldSettings>("NumericFieldSettings_Edit", model => partFieldDefinition.PopulateSettings(model));
}

You'll change it to the following:

 public override IDisplayResult Edit(ContentPartFieldDefinition partFieldDefinition)
{
   return Initialize<NumericFieldSettings>("NumericFieldSettings_Edit", model => 
   {
       var settings = partFieldDefinition.GetSettings<NumericFieldSettings>();
       model.Hint = settings.Hint;
       // ...
   });
}

Queries

Previously, any query type had to inherit from Query and required its own distinct type (e.g., SqlQuery, LuceneQuery, ElasticQuery). However, with pull request #16402, creating a custom type for each query type is no longer necessary. This update involved changes to the IQueryManager and IQuerySource interfaces, as well as the Query class. Additionally, a new project, OrchardCore.Queries.Core, was introduced.

A migration process has been implemented to transition existing queries into the new structure, ensuring no impact on existing tenants.

Key Changes

  • Modification of Interfaces and Classes: Updates were made to the IQueryManager, IQuerySource interface, and Query class to accommodate the new structure.
  • Addition of OrchardCore.Queries.Core: This new project supports the updated query handling mechanism.

Implementing IQuerySource

We now request implementations of IQuerySource using keyed services. Below is an example of how to register new implementations:

services.AddQuerySource<SqlQuerySource>(SqlQuerySource.SourceName);

This approach allows for more flexible and modular query handling in OrchardCore.

Handlers

We now have handlers for dealing with queries. To manipulate a query using a handler, implement the IQueryHandler interface. This provides a structured way to extend and customize the behavior of queries within the framework.

Twitter Module

The Twitter module has been rebranded to X.

If you were using the OrchardCore_Twitter configuration key to configure the module, please update the configuration key to OrchardCore_X. The OrchardCore_Twitter key continues to work but will be deprecated in a future release.

Users Module

  • The Login.cshtml has undergone a significant revamp. The previous AfterLogin zone, which allowed filters for injecting shapes, has been replaced. Now, you can inject shapes using drivers by implementing IDisplayDriver<LoginForm>. For example, the 'Forgot Password?' link is injected using the following driver:
public class ForgotPasswordLoginFormDisplayDriver : DisplayDriver<LoginForm>
{
    private readonly ISiteService _siteService;

    public ForgotPasswordLoginFormDisplayDriver(ISiteService siteService)
    {
        _siteService = siteService;
    }

    public override async Task<IDisplayResult> EditAsync(LoginForm model, BuildEditorContext context)
    {
        var settings = await _siteService.GetSettingsAsync<ResetPasswordSettings>();

        if (!settings.AllowResetPassword)
        {
            return null;
        }

        return View("LoginFormForgotPassword_Edit", model).Location("Links:5");
    }
}
  • The ForgotPassword.cshtml has undergone a significant revamp. The previous AfterForgotPassword zone, which allowed filters for injecting shapes, has been replaced. Now, you can inject shapes using drivers by implementing IDisplayDriver<ForgotPasswordForm>. For example, the ReCaptcha shape is injected using the following driver:
public class ReCaptchaForgotPasswordFormDisplayDriver : DisplayDriver<ForgotPasswordForm>
{
    private readonly ISiteService _siteService;

    public ReCaptchaForgotPasswordFormDisplayDriver(ISiteService siteService)
    {
        _siteService = siteService;
    }

    public override async Task<IDisplayResult> EditAsync(ForgotPasswordForm model, BuildEditorContext context)
    {
        var settings = await _siteService.GetSettingsAsync<ReCaptchaSettings>();

        if (!settings.IsValid())
        {
            return null;
        }

        return View("FormReCaptcha_Edit", model).Location("Content:after");
    }
}
  • The ResetPassword.cshtml has undergone a significant revamp. The previous AfterResetPassword zone, which allowed filters for injecting shapes, has been replaced. Now, you can inject shapes using drivers by implementing IDisplayDriver<ResetPasswordForm>. For example, the ReCaptcha shape is injected using the following driver:
public class ReCaptchaResetPasswordFormDisplayDriver : DisplayDriver<ResetPasswordForm>
{
    private readonly ISiteService _siteService;

    public ReCaptchaResetPasswordFormDisplayDriver(ISiteService siteService)
    {
        _siteService = siteService;
    }

    public override async Task<IDisplayResult> EditAsync(ResetPasswordForm model, BuildEditorContext context)
    {
        var settings = await _siteService.GetSettingsAsync<ReCaptchaSettings>();

        if (!settings.IsValid())
        {
            return null;
        }

        return View("FormReCaptcha_Edit", model).Location("Content:after");
    }
}

Previously, users were only able to reset their password through email when the "Reset Password" feature was enabled. However, we've enhanced this functionality to offer users the flexibility of resetting their password using either their email or username. Consequently, the Email property on both the ForgotPasswordViewModel and ResetPasswordViewModel have been deprecated and should be replaced with the new UsernameOrEmail property for password resets.

  • The Register.cshtml has undergone a significant revamp. The previous AfterRegister zone, which allowed filters for injecting shapes, has been replaced. Now, you can inject shapes using drivers by implementing IDisplayDriver<RegisterUserForm>. For example, the ReCaptcha shape is injected using the following driver:
public class RegisterUserFormDisplayDriver : DisplayDriver<RegisterUserForm>
{
    private readonly ISiteService _siteService;

    public RegisterUserFormDisplayDriver(ISiteService siteService)
    {
        _siteService = siteService;
    }

    public override async Task<IDisplayResult> EditAsync(RegisterUserForm model, BuildEditorContext context)
    {
        var settings = await _siteService.GetSettingsAsync<ReCaptchaSettings>();

        if (!settings.IsValid())
        {
            return null;
        }

        return View("FormReCaptcha_Edit", model).Location("Content:after");
    }
}
  • Two-factor authentication (2FA) is now available for all users. Previously, only users with permission to access the admin area could set up their own 2FA methods, although all users could still use 2FA if it was set up by an admin. To enable this change, the method IsRequiredAsync() in both the ITwoFactorAuthenticationHandlerCoordinator and ITwoFactorAuthenticationHandler interfaces has been updated to IsRequiredAsync(IUser user).

  • A new UserConfirmedEvent workflow event is now available. This event is triggered when a user successfully confirms their email address using the link sent during user registration.

  • We are introducing a new interface, IUserTimeZoneService, to replace the existing UserTimeZoneService. If your project directly injects UserTimeZoneService, you will need to switch to using IUserTimeZoneService instead.

Notifications Module

The endpoint for marking notifications as read has been updated. Previously the route was /api/notifications/read, and now it is /Notifications/MarkAsRead. This change will only affect you if you have overridden the UserNotificationNavbar view.

Contents

The IContentManager interface was modified. The method Task<IEnumerable<ContentItem>> GetAsync(IEnumerable<string> contentItemIds, bool latest = false) was removed. Instead use the method that accepts VersionOptions by providing either VersionOptions.Latest or VersionOptions.Published will be used by default.

Additionally, the GetContentItemByHandleAsync(string handle, bool latest = false) and GetContentItemByIdAsync(string contentItemId, bool latest = false) were removed from IOrchardHelper. Instead use the method that accepts VersionOptions by providing either VersionOptions.Latest or VersionOptions.Published will be used by default.

Lastly, we dropped support for AllVersions option in VersionOptions. Instead, you can use the new GetAllVersionsAsync(string contentItemId) method on IContentManager.

Media Indexing

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.

SMS Module

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.

Display Management

In this release, the signatures of the UpdateAsync() method within the SectionDisplayDriver base class have undergone modifications. Previously, these signatures accepted the BuildEditorContext parameter. However, with this update, all signatures now require the UpdateEditorContext instead. This alteration necessitates that every driver inheriting from this class adjusts their contexts accordingly.

Here are the updated signatures:

  1. From: Task<IDisplayResult> UpdateAsync(TModel model, BuildEditorContext context) To: Task<IDisplayResult> UpdateAsync(TModel model, UpdateEditorContext context)

  2. From: Task<IDisplayResult> UpdateAsync(TModel model, TSection section, BuildEditorContext context) To: Task<IDisplayResult> UpdateAsync(TModel model, TSection section, UpdateEditorContext context)

  3. From: Task<IDisplayResult> UpdateAsync(TSection section, BuildEditorContext context) To: Task<IDisplayResult> UpdateAsync(TSection section, UpdateEditorContext context). This method is now obsolete.

These adjustments ensure compatibility and adherence to the latest conventions within the DisplayDriver class.

Additionally, we've enhanced display performance by optimizing the display drivers. The following methods have been marked as obsolete:

  • IDisplayResult Display(TModel model)
  • IDisplayResult Edit(TModel model)

the following methods have been removed, with updated alternatives provided:

  • Task<IDisplayResult> DisplayAsync(TModel model, IUpdateModel updater) → Use DisplayAsync(TModel model, BuildDisplayContext context) where the Updater property is available via context.
  • IDisplayResult Display(TModel model, IUpdateModel updater) → Use Display(TModel model, BuildDisplayContext context) where the Updater property is available via context.
  • Task<IDisplayResult> UpdateAsync(TModel model, IUpdateModel updater) → Use UpdateAsync(TModel model, UpdateEditorContext context) where the Updater property is available via context.
  • Task<IDisplayResult> EditAsync(TModel model, IUpdateModel updater) → Use EditAsync(TModel model, BuildEditorContext context) where the Updater property is available via context.
  • IDisplayResult Edit(TModel model, IUpdateModel updater) → Use Edit(TModel model, BuildEditorContext context) where the Updater property is available via context.

In addition, we've introduced new static methods in the DisplayDriverBase class:

  • Task<IDisplayResult> CombineAsync(params IDisplayResult[] results)
  • Task<IDisplayResult> CombineAsync(IEnumerable<IDisplayResult> results)

The same refactoring applies to the SectionDisplayDriver class. The following methods are now obsolete:

  • Display(TSection section, BuildDisplayContext context)
  • Display(TSection section)
  • Edit(TSection section, BuildEditorContext context)
  • UpdateAsync(TSection section, UpdateEditorContext context)
  • UpdateAsync(TSection section, IUpdateModel updater, string groupId)

And the following methods have been removed, with updated alternatives:

  • UpdateAsync(TModel model, TSection section, IUpdateModel updater, UpdateEditorContext context) → Use UpdateAsync(TModel model, TSection section, UpdateEditorContext context) where the Updater is available via context.
  • UpdateAsync(TSection section, IUpdateModel updater, UpdateEditorContext context) → Use UpdateAsync(TModel model, TSection section, UpdateEditorContext context) where the Updater is available via context.
  • UpdateAsync(TSection section, IUpdateModel updater, string groupId) → Use UpdateAsync(TModel model, TSection section, UpdateEditorContext context) where the Updater and GroupId properties are available via context.
  • UpdateAsync(TSection section, UpdateEditorContext context) → Use UpdateAsync(TModel model, TSection section, UpdateEditorContext context) where the Updater is available via context.
  • EditAsync(TSection section, BuildEditorContext context) → Use EditAsync(TModel model, TSection section, BuildEditorContext context) where the Updater is available via context.
  • DisplayAsync(TSection section, BuildDisplayContext context) → Use DisplayAsync(TModel model, TSection section, BuildDisplayContext context) where the Updater is available via context.

!!! note To enhance your project's performance, address any warnings generated by the use of these obsolete methods. Please note that these methods will be removed in the next major release.

A new SiteDisplayDriver base class was introduced to simplify the process of adding a UI settings.

!!! note With the new SiteDisplayDriver base class for custom settings, you no longer need explicit checks like context.GroupId.Equals(GroupId, StringComparison.OrdinalIgnoreCase) as the base class handles this check for you.

A new extension has been introduced to simplify the registration of site setting drivers. Instead of using service.AddScoped<IDisplayDriver<ISite>, CustomSettingsDisplayDriver>(), you can now register it with the more streamlined service.AddSiteDisplayDriver<CustomSettingsDisplayDriver>().

Similarly, the ContentPartDisplayDriver base class have undergone modifications. The following methods were marked obsolete:

  • IDisplayResult Display(TPart part)
  • IDisplayResult Edit(TPart part)
  • Task<IDisplayResult> UpdateAsync(TPart part, IUpdateModel updater)

At the same time, the following method was removed:

  • Task<IDisplayResult> UpdateAsync(TPart part, IUpdateModel updater)

Moreover, the ContentFieldDisplayDriver base class have undergone modifications. The following methods were marked obsolete:

  • UpdateAsync(TField field, IUpdateModel updater, UpdateFieldEditorContext context)
  • Update(TField field, IUpdateModel updater, UpdateFieldEditorContext context)

At the same time, the following method was added:

  • UpdateAsync(TField field, UpdateFieldEditorContext context)

GraphQL Module

The GraphQL schema may change because fields are now always added to the correct part. Previously, additional fields may have been added to the parent content item type directly.

You may have to adjust your GraphQL queries in that case.

Additionally, the GetAliases method in the IIndexAliasProvider interface is now asynchronous and has been renamed to GetAliasesAsync. Implementations of this interface should be modified by updating the method signature and ensure they handle asynchronous operations correctly.

Media fields

The schema for media fields has been updated. Instead of separate lists for fileNames, urls, paths, and mediatexts, these fields are now grouped under a single files type to simplify data binding on the front end.

Please update your existing queries as shown in the following example:

Before:

{
  article {
    contentItemId
    image {
      mediatexts(first: 10)
      urls(first: 10)
    }
  }
}

After:

{
  article {
    contentItemId
    image {
      files(first: 10) {
        mediaText
        url
      }
    }
  }
}

A compatibility switch has been introduced to allow continued use of the old fileNames, urls, paths, and mediatexts fields. To restore the legacy fields, add the following AppContext switch to your startup class:

AppContext.SetSwitch("OrchardCore.Media.EnableLegacyMediaFieldGraphQLFields", true);

Resource Management

Previously the <resources type="..." /> Razor tag helper and the {% resources type: "..." %} Liquid tag were only capable of handling a hard-coded set of resource definition types (script, stylesheet, etc). Now both can be extended with IResourcesTagHelperProcessor to run custom rendering logic. To make this possible, the OrchardCore.ResourceManagement.TagHelpers.ResourceType and OrchardCore.Resources.Liquid.ResourcesTag.ResourceType enums have been replaced with a common OrchardCore.ResourceManagement.ResourceTagType. It was renamed to avoid confusion with ResourceDefinition.Type. This change does not affect the uses of the Razor tag helper or the Liquid tag in templates of user projects, but it affects published releases such as NuGet packages. Any themes and modules that contain <resources type="..." /> in a Razor file (e.g. Layout.cshtml) must be re-released to generate the updated .cshtml.cs files, because the old pre-compiled templates would throw MissingMethodException.

Workflows Module

Previously, the CreateContentTask, RetrieveContentTask, and UpdateContentTask in the workflow's CorrelationId was updated each time with the ContentItemId of the current content item. Now, the CorrelationId value will only be updated if the current workflow's CorrelationId is empty.

Additionally, a new workflow-scoped script function setCorrelationId(id:string): void was added, that you can use to update the workflow's CorrelationId.

Navigations Module

The BuildNavigationAsync method in the INavigationProvider interface was optimized by changing its return type from Task to ValueTask.

Change Logs

Azure AI Search Module

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.

New ImageSharp Image Caches for Azure Blob and AWS S3 Storage

The Microsoft Azure Media and Amazon S3 Media modules have new features for replacing the default PhysicalFileSystemCache of ImageSharp that stores resized images in the local App_Data folder. Instead, you can now use Azure Blob Storage with the Azure Media ImageSharp Image Cache feature (that utilizes AzureBlobStorageImageCache), and AWS S3 with the Amazon Media ImageSharp Image Cache feature (that utilizes AWSS3StorageCache). Depending on your use case, this can provide various advantages. Check out the Azure Media and the Amazon S3 Media docs for details.

Deployment Module

New Extensions

Added new extensions to make registering custom deployment step easier:

  • services.AddDeployment<TSource, TStep>().
  • services.AddDeployment<TSource, TStep, TDisplayDriver>().
  • services.AddDeploymentWithoutSource<TStep, TDisplayDriver>().

Recipe Executions

Before this release, implementations of IRecipeStepHandler or IRecipeEventHandler would throw exceptions to report errors if something failed to import. However, this is no longer the recommended approach for error reporting.

Now, to handle errors, we have introduced a new property named Errors in the RecipeExecutionContext. This property allows you to log errors instead of throwing exceptions. These errors should be localized and must not contain any sensitive data, as they are visible to the end user. Exceptions are still used for logging additional information, but these are not shown to the end user.

Additionally, if an error occurs, a new custom exception, RecipeExecutionException, is thrown. For more info visit the related pull-request.

Workflows Module

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.

New Events

The following events were added:

  • UserConfirmedEvent: this event triggers when a user successfully confirms their email address after registration.

GraphQL Module

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 GraphQL module now allows for filtering content fields, making it easier to query specific content fields using GraphQL. This enhancement relies on having the OrchardCore.ContentFields.Indexing.SQL module enabled.

To filter content items based on a specific content field, you can use a query like this:

product(where: {price: {amount_gt: 10}}) {
    contentItemId
    displayText
    price {
        amount
    }
}

Email Module

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 inject IEmailService instead.
  • The SMTP related services are now part of a new module named OrchardCore.Email.Smtp. To use the SMTP provider for sending emails, enable the OrchardCore.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 to OrchardCore_Email_Smtp. The OrchardCore_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.

Menu

Menus and AdminMenus now support specifying the target property.

Admin Menu

The admin menu has been optimized with significant performance improvements and code enhancements. When adding a navigation provider to the admin menu, it is now recommended to use the new AdminNavigationProvider class rather than directly implementing the INavigationProvider interface in your project.

Furthermore, when passing route values to actions, it's a best practice to store these values in constant variables for maintainability and clarity. Below is an example demonstrating this approach:

public sealed class AdminMenu : AdminNavigationProvider
{
    private static readonly RouteValueDictionary _routeValues = new()
    {
        { "area", "OrchardCore.Settings" },
        { "groupId", AdminSiteSettingsDisplayDriver.GroupId },
    };

    internal readonly IStringLocalizer S;

    public AdminMenu(IStringLocalizer<AdminMenu> stringLocalizer)
    {
        S = stringLocalizer;
    }

    protected override ValueTask BuildAsync(NavigationBuilder builder)
    {
        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 ValueTask.CompletedTask;
    }
}

A new extension has been introduced to simplify the registration of navigation providers. Instead of using services.AddNavigationProvider<AdminMenu>(), you can now register it with the more streamlined services.AddNavigationProvider<AdminMenu>();.

Admin Routes

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 sealed 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).

Users Module

Added a new User Localization feature that allows to be able to configure the culture per user from the admin UI.

Navbar

Added a new Navbar() function to Liquid to allow building the Navbar shape using Liquid. Here are the steps needed to add the Navbar shape into your custom Liquid shape:

  1. Construct the shape at the beginning of the layout.liquid file to enable navbar items to potentially contribute to the resources output as necessary.
{% assign navbar = Navbar() | shape_render %}
  1. Subsequently in the layout.liquid file, invoke the shape at the location where you want to display it.
{{ navbar }}

Notifications Module

TheINotificationMessage interface was updated to includes the addition of a Subject field, which facilitates the rendering of notification titles. Moreover, the existing Summary field has been transitioned to HTML format. This adjustment enables the rendering of HTML notifications in both the navigation bar and the notification center. Consequently, HTML notifications can now be created, affording functionalities such as clickable notifications.

Furthermore, the introduction of the NotificationOptions provides configuration capabilities for the notifications module. This structure comprises the following attributes:

  • TotalUnreadNotifications: This property specifies the maximum number of unread notifications that are displayed in the navigation bar. By default, it is set to 10.
  • DisableNotificationHtmlBodySanitizer: Notifications generated from workflows have their HtmlBody sanitized by default. This property allows you to disable the sanitization process if needed.
  • AbsoluteCacheExpirationSeconds: Specifies the absolute maximum duration, in seconds, for which the top unread user notifications are cached when caching is enabled. A value of 0 does not disable caching but indicates that there is no fixed expiration time for the cache. You can set this value to define a maximum lifespan for the cached data before it is invalidated.
  • SlidingCacheExpirationSeconds: Defines the sliding cache duration, in seconds, for unread user notifications when caching is enabled. By default, the cache is refreshed and extended for up to 1800 seconds (30 minutes) after each user activity. To disable sliding expiration, you can set this value to 0.

Secure media files

Introduces a new "Secure Media" feature for additional control over who can access media files. For more info read the Secure Media docs.

Users Module

Enhanced functionality has been implemented, giving developers the ability to control the expiration time of different tokens, such as those for password reset, email confirmation, and email change, which are sent through the email service. Below, you'll find a comprehensive list of configurable options along with their default values:

Class Name Default Expiration Value
ChangeEmailTokenProviderOptions The token is valid by default for 15 minutes.
EmailConfirmationTokenProviderOptions The token is valid by default for 48 hours.
PasswordResetTokenProviderOptions The token is valid by default for 15 minutes.

You may change the default values of these options by using the services.Configure<> method. For instance, to change the EmailConfirmationTokenProviderOptions you can add the following code to your project

services.Configure<EmailConfirmationTokenProviderOptions>(options => options.TokenLifespan = TimeSpan.FromDays(7));

Reloading Tenants

The recent addition in the Pull Request introduces the IShellReleaseManager, enabling you to initiate a shell release at the request's conclusion via the RequestRelease() method. This action is accessible from any service within the application. However, there's no assurance of immediate fulfillment since another service may utilize the same service and suspend the release request later. Should you need to suspend the request to release the shell, you can utilize the SuspendReleaseRequest() method to suspend the tenant release request and the tenant will not be released.

Reloading Tenants Display Drivers

When implementing a site settings display driver, you have the option to reload the shell (restart the tenant). If you choose to do so, manually releasing the shell context using await _shellHost.ReleaseShellContextAsync(_shellSettings) is unnecessary. Instead, you should use the new IShellReleaseManager.RequestRelease(). This ensures that the shell stays intact until all settings are validated. For instance, in ReverseProxySettingsDisplayDriver, the code was modified from this:

public class ReverseProxySettingsDisplayDriver : SiteDisplayDriver<ReverseProxySettings>
{
    //
    // For  example simplicity, other methods are not visible.
    //

    public override async Task<IDisplayResult> UpdateAsync(ISite site, ReverseProxySettings settings, UpdateEditorContext context)
    {
        var user = _httpContextAccessor.HttpContext?.User;

        if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageReverseProxySettings))
        {
            return null;
        }

        //
        // For  example simplicity, other logic are not visible.
        //

        // If the settings are valid, release the current tenant.
        if (context.Updater.ModelState.IsValid)
        {
            await _shellHost.ReleaseShellContextAsync(_shellSettings);
        }

        return await EditAsync(site, settings, context);
    }
}

To this:

public class ReverseProxySettingsDisplayDriver : SiteDisplayDriver<ReverseProxySettings>
{
    private readonly IShellReleaseManager _shellReleaseManager;
    private readonly IHttpContextAccessor _httpContextAccessor;
    private readonly IAuthorizationService _authorizationService;

    public ReverseProxySettingsDisplayDriver(
        // (1) Inject the new service.
        IShellReleaseManager shellReleaseManager
        IHttpContextAccessor httpContextAccessor,
        IAuthorizationService authorizationService)
    {
        _shellReleaseManager = shellReleaseManager;
        _httpContextAccessor = httpContextAccessor;
        _authorizationService = authorizationService;
    }

    //
    // For  example simplicity, other methods are not visible.
    //

    public override async Task<IDisplayResult> UpdateAsync(ISite site, ReverseProxySettings settings, UpdateEditorContext context)
    {
        var user = _httpContextAccessor.HttpContext?.User;

        if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageReverseProxySettings))
        {
            return null;
        }

        //
        // For  example simplicity, other logic are not visible.
        //

        // (2) Request a release at the end of the request.
        _shellReleaseManager.RequestRelease();

        return await EditAsync(site, settings, context);
    }
}

Infrastructure

Prior to this release, if you had a module with multiple features, you had to use the [Feature("your feature id")] attribute to assign IShapeTableProvider, IDataMigration, and IPermissionProvider to a specific feature of the module that had a feature ID other than the module ID. With pull-request 15793, this is no longer needed. However, [Feature("...")] is still required for the Startup class and controllers when you want the controller action to be available only when a specific feature is enabled.

Site Settings

  • New extension methods named GetSettingsAsync<T>() and GetSettingsAsync<T>("") were added to the ISiteService interface. These methods allow you to retrieve specific settings with a single line of code. For example, to get the LoginSettings, you can now use:
await _siteService.GetSettingsAsync<LoginSettings>();

Previously, achieving the same result required more code:

(await _siteService.GetSiteSettingsAsync()).As<LoginSettings>();
  • A new extension method named GetCustomSettingsAsync() was added to the ISiteService interface. This method allows you to retrieve custom settings. For example, to get custom settings of type 'BlogSettings', you can now use:
ContentItem blogSettings = await _siteService.GetCustomSettingsAsync("BlogSettings");

Previously, achieving the same result required more code:

var siteSettings = await _siteService.GetSiteSettingsAsync();
var blogSettings = siteSettings.As<ContentItem>("BlogSettings");

Content Fields

Before this release, the MarkdownField used a single-line input field by default (named the Standard editor) and offered two different editors: Multi-line with a simple textarea and WYSIWYG with a rich editor. Now, by default, it uses a textarea as the Standard editor, and the separate Multi-line option has been removed.

You have nothing to do, fields configured with the Standard or Multi-line editors previously will both continue to work. Note though, that their editors will now be a textarea.

Liquid

The Culture context variable got the new properties for a fuller Liquid culture support: DisplayName, NativeName, and TwoLetterISOLanguageName. These are the same as their .NET counterparts.

A new filter named supported_cultures was added to allow you to get a list of supported cultures. Here is an example of how to print the names of supported cultures using a list:

{% assign cultures = Culture | supported_cultures %}

<ul>
{% for culture in cultures %}
    <li>{{ culture.Name }}</li>
{% endfor %}
</ul>

Adding properties with additional tag helpers

The new <add-property> tag helper can be placed inside the <shape> tag helpers to add properties to the shape. This is similar to prop-* attributes, but you can also include Razor code as the IHtmlContent property value, which was impossible before. See more details here.

Sealing Types

Many type commonly used by modules can be sealed, which improves runtime performance. While it's not mandatory, we recommend that you consider applying this improvement to your own extensions as well. We've implemented this enhancement in the following pull-requests:

!!! note Do not seal classes that are used to create shapes like view-models. Sealing these classes can break your code at runtime, as these classes need to be unsealed to allow for proxy creation.

Workflow Trimming

The Workflows module now has a Trimming feature to automatically clean up old workflow instances. See the corresponding documentation for details.

Obsoleting TrimEnd

The methods public static string TrimEnd(this string value, string trim = "") from OrchardCore.Mvc.Utilities and OrchardCore.ContentManagement.Utilities are being obsoleted and replaced by OrchardCore.ContentManagement.Utilities.TrimEndString(this string value, string suffix). This was done to prepare the code base for the next .NET 9.0 release which has a conflicting method with a different behavior.

Miscellaneous

A new extension has been introduced to simplify the registration of permission providers. Instead of using service.AddScoped<IPermissionProvider, CustomPermissionProvider>(), you can now register it with the more streamlined service.AddPermissionProvider<CustomPermissionProvider>().

What's Changed

  • Start to 1.9-preview by @MikeAlhayek in #14976
  • Add support for Azure AI Search (Azure Cognitive Search) by @MikeAlhayek in #14925
  • Update NJsonSchema 11.0.0 by @hishamco in #14981
  • Show warnings when Azure AI Search is not configured by @MikeAlhayek in #14988
  • Use PagerAsync and PagerSlimAsync instead of using dynamic New by @MikeAlhayek in #14978
  • Update ResourceManager.cs to better handle resources that don't specify a version by @mroskamp in #14992
  • Add Keyed services support in ShellScopeServices by @MikeAlhayek in #14998
  • Update Jint 3.0.0-beta-2059 by @hishamco in #15000
  • Update BenchmarkDotNet 0.13.12 by @hishamco in #15001
  • Update xUnit 2.6.5 & xUnit Analyzers 1.9.0 by @hishamco in #15002
  • Set a default display name only when none is specified by @giannik in #15013
  • Move Media Indexing into a new module to simplify dependencies. by @MikeAlhayek in #14989
  • Fix dependencies for GraphQL by @MikeAlhayek in #15015
  • Fix Media Field dependency by @MikeAlhayek in #15017
  • Docs about the Jean-Thierry Kéchichian Community Award (Lombiq Technologies: OCORE-135) by @Piedone in #15006
  • Add request localization settings to the configured one by @hishamco in #14866
  • .NET 8.0.1 by @agriffard in #15023
  • Allow configuring Azure Search AI from UI or appsettings. by @MikeAlhayek in #15004
  • Fix pluralization arguments for admin list summaries by @thatplatypus in #14611
  • mediafield-attached media -use user friendly file name if exists by @giannik in #14782
  • Register Search Member Access Strategy in the search module by @MikeAlhayek in #15035
  • Add environment accessor in Liquid by @hishamco in #15027
  • Service cleanup in Azure AI Search by @MikeAlhayek in #15063
  • Add NullValue to IndexingConstants by @MikeAlhayek in #15072
  • Use FrozenDictionary by @MikeAlhayek in #15040
  • Update editorconfig file by @MikeAlhayek in #15038
  • Add AddDeployment extensions by @MikeAlhayek in #15069
  • Fix Display Name by @MikeAlhayek in #15080
  • Update Contributors docs (Lombiq Technologies: OCORE-138) by @Piedone in #15037
  • Microsoft.Identity.Web 2.16.1 by @agriffard in #15085
  • Add OrchardCore_Redis:DisableSSLCertificate option (#15077) by @ovekaaven in #15084
  • Update DocumentFormat.OpenXml 3.0.1 by @hishamco in #15091
  • fix: title empty if AddSegment is not called by @PiemP in #14295
  • Add ISmsService and support multiple SMS Providers by @MikeAlhayek in #14774
  • Update Jint 3.0.0-beta-2060 by @hishamco in #15110
  • Update xUnit 2.6.6 & xUnit Analyzers 1.10.0 by @hishamco in #15112
  • Update StackExchange.Redis 2.7.17 by @hishamco in #15111
  • Added feature ID in braces to feature name for clarity by @brunoAltinet in #10391
  • Fix logged in user style in RTL by @hishamco in #15108
  • Mention project in the OC docs (Lombiq Technologies: OCC-167) by @porgabi in #14795
  • Merge Release/1.8 on main by @MikeAlhayek in #15117
  • Fix a typo by @MikeAlhayek in #15118
  • Fix delivery method key in OrchardCore_Email section by @hishamco in #15120
  • Fix release notes format by @hishamco in #15121
  • Adding in "datetime" and "timespan" support by @jeffolmstead in #5546
  • OpenIddict 5.1.0 by @agriffard in #15122
  • Cleanup the MVC project by @MikeAlhayek in #15115
  • yessql.db -> OrchardCore.db by @hishamco in #7446
  • Update Jint 3.0.0 by @hishamco in #15132
  • Prevent frontend search if Azure AI is not configured by @MikeAlhayek in #15135
  • Update AngleSharp 1.1.0 by @hishamco in #15139
  • Sqllite DatabaseName was not read from appsettings by @microposmp in #15138
  • Move rule service interfaces into OC.Rule.Services namespace by @hishamco in #13431
  • Admin Template view not found. by @microposmp in #15141
  • Replace SiteNameValidAttribute with StringLengthAttribute by @hishamco in #13434
  • Use Microsoft.Extensions.Http.Resilience by @hishamco in #14712
  • Remove obsolete code by @TFleury in #12955
  • Fixing accessibility problems and HTML validation errors in built-in themes (Lombiq Technologies: OCORE-83) by @DemeSzabolcs in #11243
  • Cleanup ISmsService by @MikeAlhayek in #15142
  • Fix TheAdminTheme layout margin and padding by @MikeAlhayek in #15143
  • Fix SectionDisplayDriver prefix by @MikeAlhayek in #15123
  • Prefill template name when creating a template. by @microposmp in #15145
  • Update the height of the admin content by @MikeAlhayek in #15153
  • Eliminate the anti-discovery pattern in Elasticsearch by @MikeAlhayek in #15134
  • Renaming and cleaning up search services by @MikeAlhayek in #15156
  • Revert "yessql.db -> OrchardCore.db (#7446)" by @sebastienros in #15167
  • Make Full-Text and Display-Text field support String Type by @MikeAlhayek in #15176
  • Update YesSql 4.1.0 by @MikeAlhayek in #15179
  • Add DiscoverableSterotypes to GraphQLContentOptions by @MikeAlhayek in #15175
  • Add jQuery as a dependency to ContentPreviewEdit (Lombiq Technologies: OCORE-142) by @Psichorex in #15183
  • Update libphonenumber-csharp 8.13.29 by @hishamco in #15185
  • use 'var' instead of explicit type (IDE0007 and IDE0008) by @MikeAlhayek in #15189
  • Simplify new expression (IDE0090) by @MikeAlhayek in #15188
  • Use concrete types when possible for improved performance by @MikeAlhayek in #15190
  • Adding Validate Pull Request GitHub Actions workflow to check PRs for merge conflicts (Lombiq Technologies: OCORE-141) by @Piedone in #15114
  • Simplify Collection initialization (IDE0300) by @MikeAlhayek in #15187
  • Cleanup exceptions by @MikeAlhayek in #15192
  • Doc text should end with period. Use Count over Any() by @MikeAlhayek in #15191
  • Add descriptive exception for Azure-Vault service by @MikeAlhayek in #15178
  • Linking Orchard Core Walkthroughs module in tutorials README (Lombiq Technologies: NEST-113) by @DemeSzabolcs in #15202
  • Cleanup Admin Menu by @MikeAlhayek in #15199
  • Adding the Issue Metrics Action to generate metrics on how we manage issues (Lombiq Technologies: OCORE-139) by @Piedone in #15041
  • Fix multiple suggestions by @MikeAlhayek in #15204
  • Make TriggerEvent retnrun WorkflowExecutionContext by @hyzx86 in #14281
  • Fix string comparison changes in YesSql queries by @sebastienros in #15212
  • Cleanup permission providers by @MikeAlhayek in #15205
  • Use ReadOnly Session when indixing content items by @MikeAlhayek in #15216
  • Wrong placement is applied by @microposmp in #15174
  • Make Sqlite database name configurable by @MikeAlhayek in #15209
  • Fix Sitemap timestamp format by @MikeAlhayek in #15229
  • Use invariant culture for rendering iso date and time by @sebastienros in #15230
  • Fix TheTheme navbar by @MikeAlhayek in #15231
  • Fix Layer rules UI by @MikeAlhayek in #15234
  • Add guidance when connecting to a SQL Server using untrusted cetrificate by @MikeAlhayek in #15210
  • Skip DeleteObjectsAsync call if there are no items to delete by @AndreySurkov in #15239
  • Implemented GraphQL query for retrieving current user data and its custom settings by @gvkries in #15215
  • Update HtmlSanitizer 8.1.839-beta by @hishamco in #15238
  • Remove Newtonsoft by @jtkech in #14572
  • Fix: #5533 - Enabled reading request body for HTTP POST requests. by @emrahtokalak in #13853
  • use nameof by @SimonCropp in #15244
  • use some collection expressions by @SimonCropp in #15250
  • simplify some linq by @SimonCropp in #15249
  • Make NullStringLocalizer & HtmlLocalizer Public by @hishamco in #10939
  • Change how AdminMenu is registered and remove trapping errors in default serialize by @MikeAlhayek in #15261
  • Update Microsoft.Identity.Web 2.17.0 by @hishamco in #15262
  • Throw when SQLite file not found by @Skrypt in #15245
  • Update Microsoft.NET.Test.Sdk 17.9.0 by @hishamco in #15268
  • Remove unnecessary argument in ThrowIfNullOrEmpty by @sebastienros in #15272
  • Fix ILocalClock perf issues by @Skrypt in #15270
  • Fix Sára's contributor profile (Lombiq Technologies: OCORE-144) by @Piedone in #15277
  • Update NodaTime 3.1.11 by @hishamco in #15278
  • Fix errors when role names have different casing by @gvkries in #15113
  • Fix search icon by @MikeAlhayek in #15279
  • Upgrade graphql to 7.7.2 by @hyzx86 in #15129
  • Remove useless placeholder file by @TFleury in #12954
  • Address multiple warnings by @MikeAlhayek in #15271
  • Refactor DbConnectionValidator by @hishamco in #12363
  • Remove hardcode from LocalizationPart shape name by @AndreySurkov in #15266
  • Update theme.md - creation of wwwroot folder was missing by @MarGraz in #15265
  • Optimize ElasticIndexingService by @MikeAlhayek in #15227
  • Select all Content Types by @Skrypt in #15285
  • Update libphonenumber-csharp 8.13.30 by @hishamco in #15295
  • Update README.md added missing parts in "From VS (manual way)" by @MarGraz in #15296
  • Update GraphQL 7.8.0 by @hishamco in #15284
  • regenerate activity URL when importing Workflows by @lampersky in #15291
  • fix enabling Amazon S3 storage provider without configuration causes exception by @neglectedvalue in #15293
  • Fix IDE warnings by @MikeAlhayek in #15300
  • Cleanup using statements by @MikeAlhayek in #15302
  • Login providers should not be visible if the service is not configured by @lampersky in #15305
  • Javascript error when there are no deployment steps by @lampersky in #15306
  • Admin route by @sarahelsaig in #15251
  • Rename AddAdminMenu extension to AddAdminNode by @MikeAlhayek in #15316
  • Prevent an unhandled exception has occurred while executing the request. by @MikeAlhayek in #15317
  • User Localization settings by @Skrypt in #13181
  • Fix recipe steps with polymorphic types by @sebastienros in #15320
  • Bump OpenIddict to 5.2.0 and replace the OpenIddict.AspNetCore metapackage by individual references by @kevinchalet in #15323
  • Decorate the interactive OpenID endpoints with [DisableCors] by @kevinchalet in #15324
  • CORS settings fix by @lampersky in #15318
  • Fix GraphQL Explorer by @MikeAlhayek in #15319
  • monaco-editor 0.46 by @agriffard in #15288
  • .NET 8.0.2 by @agriffard in #15325
  • Update Microsoft.Extensions.Azure 1.7.2 by @hishamco in #15330
  • Fix app-based TFA setup screen encoding (Lombiq Technologies: OCORE-145) by @Piedone in #15334
  • Update Azure.Extensions.AspNetCore.DataProtection.Blobs 1.3.3 by @hishamco in #15331
  • Update HtmlSanitizer 8.1.844-beta by @hishamco in #15333
  • Update Microsoft.Extensions.Http.Resilience 8.2.0 by @hishamco in #15329
  • Update Azure.Extensions.AspNetCore.Configuration.Secrets 1.3.1 by @hishamco in #15332
  • Add onload attribute to ReCaptchaTagHelper by @mroskamp in #15283
  • Fix the build in ReCaptchaTagHelper by @hishamco in #15339
  • Add CustomSettingsConstants by @hishamco in #15282
  • Remove obsolete CustomSettingsService methods by @hishamco in #15280
  • Update xUnit packages by @hishamco in #15343
  • Use IHttpClientFactory.CreateClient() instead of static HttpClient by @hishamco in #15350
  • Add warnings for CORS policies by @giannik in #15258
  • Markdig 0.35.0 by @agriffard in #15352
  • Fix workflow icons by @MikeAlhayek in #15370
  • Add docs for Full-Text Search for Admin UI by @MikeAlhayek in #15369
  • Add multiple constructors for ListValueOption by @MikeAlhayek in #15368
  • Update StackExchange.Redis 2.7.20 by @hishamco in #15379
  • Update the ContentElement constructor by @MikeAlhayek in #15365
  • DisplayAsync to return actual pre-rendered IHtmlContent by @ns8482e in #15247
  • Register JsonSerializerOptions in the IoC Container by @MikeAlhayek in #15328
  • Fix CORS assets by running gulp by @hishamco in #15380
  • Support comments in recipes by @MikeAlhayek in #15386
  • Bootstrap 5.3.3 by @agriffard in #15374
  • Fix steporder.js by @MikeAlhayek in #15387
  • Remove benchmark summary comments by @hishamco in #15393
  • Expose CultureDictionaryRecordKey properties by @hishamco in #11358
  • Update Jint 3.0.1 by @hishamco in #15402
  • Update libphonenumber-csharp 8.13.31 by @hishamco in #15403
  • Update Serilog.AspNetCore 8.0.0 by @hishamco in #15151
  • Update StackExchange.Redis 2.7.23 by @hishamco in #15409
  • Support Create and CreateAsync in DataMigrationManager by @MikeAlhayek in #15396
  • Azure Email Communication Services (Lombiq Technologies: OCORE-129) by @MikeAlhayek in #15254
  • Reduce Constructor Injections by @MikeAlhayek in #15395
  • Update Serilog.AspNetCore 8.0.1 by @hishamco in #15410
  • Fix Smtp namespace by @hishamco in #15418
  • Use HashCode.Combine in FeatureHash by @hishamco in #15414
  • Add WithPart<> and WithSettings<> builder methods by @MikeAlhayek in #15426
  • Update Microsoft.Identity.Web 2.17.1 by @hishamco in #15427
  • Update StackExchange.Redis 2.7.27 by @hishamco in #15428
  • Add cache busting parameter to media thumbnails and links in Media Library (Lombiq Technologies: OCORE-143) by @Piedone in #15276
  • Update AngleSharp 1.1.1 by @hishamco in #15433
  • Add LoginInfoConverter by @hyzx86 in #15422
  • Check SubResource Integrity by @hishamco in #9947
  • fix(AuditTrail): allow to see action option only if user have view au… by @PiemP in #15432
  • Update MailKit & MimeKit 4.4.0 by @hishamco in #15437
  • Avoid null reference exception by @MikeAlhayek in #15420
  • Update OpenIddict 5.3.0 by @hishamco in #15453
  • Use SHA384.HashDataAsync by @hishamco in #15461
  • Fix script tag helper dependencies inheriting the script's HTML attributes by @sarahelsaig in #15438
  • Update SixLabors.ImageSharp.Web 3.1.1 by @hishamco in #15468
  • Update README.md Custom User Settings by @MarGraz in #15454
  • Fix the serialization of Feature profile document by @MikeAlhayek in #15465
  • Configure JsonOptions by default by @MikeAlhayek in #15460
  • Author field perhaps is null by @hyzx86 in #15457
  • Remove TitlePart wrapper when it is hidden by @douwinga in #15471
  • Fix log file path based on ORCHARD_APP_DATA environment variable by @ns8482e in #15364
  • Update Manifest.cs description of "OpenID Authorization Server" feature by @MarGraz in #15407
  • Use ContentSerializerJsonOptions by @MikeAlhayek in #15474
  • Fix Contributor Map workflow (Lombiq Technologies: OCORE-146) by @Piedone in #15349
  • Update AngleSharp 1.1.2 by @hishamco in #15476
  • Set Owner field in graphql null able by @hyzx86 in #15477
  • Add LoginInfoJsonConverter to ContentSerializerJsonOptions by @hyzx86 in #15480
  • Don't run SRI locally be default by @hishamco in #15486
  • Update Fluid 2.6.0 by @hishamco in #15489
  • Issue 15492 bugfix: Prevent session connection closing in WorkflowTypeController by @M-Lipin in #15495
  • Add docs for ThemeSelectorResult by @hishamco in #11147
  • .NET 8.0.3 by @agriffard in #15493
  • Update README.md, Custom User Settings, wrong link to the video by @MarGraz in #15487
  • Update StackExchange.Redis 2.7.33 by @hishamco in #15503
  • Adjust text of marker file hint (8270) by @w-ko in #15392
  • Update Microsoft.Extensions.Http.Resilience 8.3.0 by @hishamco in #15494
  • Make CultureDictionaryRecordKey record struct by @hishamco in #15413
  • Fix functional test command docs by @hishamco in #15398
  • Update the jint options to support System.text.json by @hyzx86 in #15449
  • Markdig 0.36.0 by @agriffard in #15513
  • Update libphonenumber-csharp 8.13.32 by @hishamco in #15515
  • Update Microsoft.Identity.Web 2.17.2 by @hishamco in #15517
  • Update Markdig 0.36.2 by @hishamco in #15516
  • Update DocumentFormat.OpenXml 3.0.2 by @hishamco in #15523
  • Add OrchardCoreConstants by @hishamco in #13438
  • Update the Readme and add a way to get in touch by @MikeAlhayek in #15531
  • Dojo Course 3 videos in docs (Lombiq Technologies: OCORE-137) by @porgabi in #15475
  • Update Fluid 2.7.0 by @hishamco in #15542
  • Update GraphQLFilter example by @MikeKry in #12843
  • Update HtmlSanitizer 8.1.860-beta by @hishamco in #15550
  • Update Facebook link on Readme by @MikeAlhayek in #15559
  • Add missing Cloudsmith attribution (Lombiq Technologies: OCORE-150) by @Piedone in #15547
  • Remove unnecessary check in AzureAISearchIndexingService by @hishamco in #15555
  • Update OpenId module, write more information to log by @hyzx86 in #10641
  • Fix menu item deletion by @hishamco in #15500
  • Update JOptions configuration (Lombiq Technologies: OCORE-149) by @sarahelsaig in #15534
  • Fix graphql api with parameters by @hyzx86 in #15529
  • Use JsonPath.Net for JSONPath selectors (Lombiq Technologies: OCORE-148) by @sarahelsaig in #15524
  • Add Navbar shape for Liquid by @MikeAlhayek in #15532
  • Log graphql error by @hyzx86 in #15544
  • Set listpart property in listpartviewmodel by @aliamiras in #15560
  • Consolidate admin required fields by @xtomas in #15545
  • add porgabi as a contributor for code by @allcontributors in #15566
  • Allows to serialize 'SitemapSource' derived types by @MikeAlhayek in #15571
  • Minimal API by @MikeAlhayek in #15294
  • Add SelectNode and Remove method to JsonDynamicObject class by @hyzx86 in #15509
  • Add a way to hide ContentTypeDefinitionSettings by @MikeAlhayek in #15472
  • Fix Input Widget css style and add alternate for Widget-Forms to allow customization by @MikeAlhayek in #15563
  • Update 1.9.0 Release notes after STJ recent changes by @MikeAlhayek in #15587
  • Update ZString 2.6.0 by @hishamco in #15592
  • Update libphonenumber-csharp 8.13.33 by @hishamco in #15591
  • Restore user credentials in HttpBackgroundJob by @hyzx86 in #14329
  • Convert Themes views to shapes by @MikeAlhayek in #15589
  • Rename Method in MinimalAPI by @MikeAlhayek in #15594
  • Named Part Fields are not indexed by @lampersky in #14366
  • Cleanup Navbar by @MikeAlhayek in #15595
  • Update OpenIddict 5.4.0 by @MikeAlhayek in #15597
  • Verify the return type in migration methods prior to invoking them by @MikeAlhayek in #15596
  • Avoid NRE in ElasticQueryService.SearchAsync() by @hishamco in #15600
  • Update Microsoft.Identity.Web 2.17.3 by @hishamco in #15605
  • Check the null for searchResult.Value before call GetResultsAsync() by @hishamco in #15598
  • Fix Taxonomy Serialization by @MikeAlhayek in #15615
  • Ensure Search Icon shows up on any search module by @MikeAlhayek in #15611
  • Improve notification filter performance by @sebastienros in #15610
  • Adjust content type condition to evaluate to true when no content is displayed by @rwawr in #15603
  • Improve the Pager extensions by @MikeAlhayek in #15617
  • Document the removal of PopulateSettings in 1.9 release notes by @MikeAlhayek in #15618
  • Publishing preview packages daily (Lombiq Technologies: OCORE-151) by @Piedone in #15552
  • Fix HTML style for ThemeEntry by @MikeAlhayek in #15609
  • Do not convert IShape to dynamic by @MikeAlhayek in #15622
  • Remove unnecessary FrozenDictionary by @sebastienros in #15623
  • Update Microsoft.Identity.Web 2.17.4 by @hishamco in #15626
  • Refine hashing usages by @sebastienros in #15621
  • Remove unnecessary null coalescing in settings.MergeArrayHandling by @hishamco in #15599
  • Improve ShapeTableManager performance by @sebastienros in #15620
  • Cleanup Serializers by @MikeAlhayek in #15633
  • Add HTML support to notification summary by @MikeAlhayek in #15607
  • Remove obsolete methods from INotifier by @MikeAlhayek in #15631
  • Save byte array allocations by @sebastienros in #15630
  • Simplify ShapeDescriptorIndex by @MikeAlhayek in #15632
  • Use FrozenDictionaries in ShapeTables by @sebastienros in #15651
  • Some improvements around building common shapes by @MikeAlhayek in #15652
  • fontawesome 6.5.2 by @agriffard in #15656
  • Update Fluid 2.8.0 by @hishamco in #15660
  • Upgrade to Jint 3.0.2 by @lahma in #15667
  • Seal private and internal types by @lahma in #15668
  • Microsoft Entra ID authentication with multi-tenancy by @gvkries in #14803
  • Fix Content Drivers by @MikeAlhayek in #15680
  • Fix Content Culture Picker by @MikeAlhayek in #15681
  • Add extensions to register conditions in the Rules module by @MikeAlhayek in #15645
  • Remove redirect to index after settings update by @M-Lipin in #15679
  • Update libphonenumber-csharp 8.13.34 by @hishamco in #15669
  • Several perf improvements around shape processing by @sebastienros in #15661
  • Implement local storage emulator support for the Amazon S3 Media module, documentation (Lombiq Technologies: OCORE-153) by @Piedone in #15677
  • Add missing string comparison type to equals comparisons by @lahma in #15675
  • Trumbowyg 2.28.0 by @agriffard in #15699
  • Suppress incorrect "CA1822:Mark members as static" analyzer violation in ShapeProxyBenchmark (Lombiq Technologies: OCORE-156) by @Piedone in #15707
  • .NET 8.0.4 by @agriffard in #15704
  • Add Redis Health Check by @hishamco in #13589
  • Fix analyzer errors on latest net9.0 SDK by @sebastienros in #15712
  • Fix AISearch index prefix and index creation by @MikeAlhayek in #15723
  • Update GitHub Actions actions to the latest versions by @MikeAlhayek in #15619
  • Add LoginForm Shapes by @MikeAlhayek in #15701
  • Fix Issue-15709: Fix NullReferenceException in CacheTicketStore.RenewAsync method by @M-Lipin in #15711
  • Reduce contention by caching shellsetting accessors by @sebastienros in #15728
  • Update Azure.Identity by @infofromca in #15746
  • Update xunit.analyzers 1.12.0 by @hishamco in #15748
  • Update xUnit 2.7.1 by @hishamco in #15747
  • Update xunit.runner.visualstudio 2.5.8 by @hishamco in #15749
  • Configure Token provider from the corresponding provider by @MikeAlhayek in #15627
  • Fix shortcode button with easy mde editor by @Skrypt in #15741
  • Update Microsoft.Extensions.Http.Resilience 8.4.0 by @hishamco in #15750
  • Use constants for the common role names by @MikeAlhayek in #15751
  • Brand Twitter to X by @hishamco in #15528
  • allow users to setup default content type settings visibility by @lampersky in #15733
  • Add StringExtensions Unit Tests by @hishamco in #14099
  • Add MariaDB support docs by @infofromca in #15744
  • Use static typing in ContentFieldsProvider instead of dynamic. by @gvkries in #15763
  • Update localization README regarding pluralization by @rjpowers10 in #13141
  • Assign menu children correctly from MenuPartDisplayDriver by @MikeAlhayek in #15766
  • Collapsed fields collision in graphql by @lampersky in #13927
  • Fix Monaco and Trumbowyg editor settings by @MikeAlhayek in #15768
  • ImageSharp.Web 3.1.2 by @agriffard in #15767
  • Jint 3.1.0 by @agriffard in #15730
  • Display selected culture as native name in content culture picker by @hishamco in #15776
  • Removing unnecessary and outdated CODEOWNERS (Lombiq Technologies: OCORE-154) by @Piedone in #15692
  • Adding GitHub Actions workflow to close stale PRs (Lombiq Technologies: OCORE-154) by @Piedone in #15710
  • Update MailKit & MimeKit to 4.5.0 by @hishamco in #15769
  • Fix ConcurrencyException with Sitemaps when saving two content items at once (Lombiq Technologies: GOV-33) by @Piedone in #15777
  • Fix Dockerfile to allow successful build by @MikeAlhayek in #15784
  • Fix frontend assets being outdated (Lombiq Technologies: OCORE-158) by @Piedone in #15735
  • Remove ReCaptchaLoginFilter by @MikeAlhayek in #15736
  • Display Drivers validation issue by @Skrypt in #15488
  • 13715: Added edit button to the ContentPickerField Edit view by @dannyd89 in #13764
  • Treat warnings as errors during CI build (Lombiq Technologies: OCORE-157) by @Piedone in #15764
  • Update Azure.Identity 1.11.1 by @hishamco in #15791
  • Update Azure.Extensions.AspNetCore.DataProtection.Blobs 1.3.4 by @hishamco in #15790
  • Update Microsoft.Identity.Web 2.17.5 by @hishamco in #15789
  • Update Microsoft.Extensions.Azure 1.7.3 by @hishamco in #15788
  • Update libphonenumber-csharp 8.13.35 by @hishamco in #15795
  • Add OC.HealthChecks.Abstractions, and health Checks options to show details by @hishamco in #13590
  • Fixing InvalidOperationException when using responseWrite scripting method by @lampersky in #12213
  • Add TypePartDefinition to BagPartEditViewModel by @giannik in #15798
  • Avoid null exception when href is null. by @MikeAlhayek in #15765
  • Refresh and simplify mkdocs instructions by @sarahelsaig in #15802
  • Makes the GraphQL user type reusable. by @gvkries in #15691
  • Update ContentUpdateTask, add SetCorrelationId function by @hyzx86 in #10928
  • Update Azure.Identity 1.11.2 by @hishamco in #15801
  • Release notes about SectionDisplayDriver breaking changes by @Skrypt in #15792
  • Add missing contributors (Lombiq Technologies: OCORE-154) by @Piedone in #15693
  • Fix unrecognized relative links in the docs and GitHub Actions workflow to validate building the documentation (Lombiq Technologies: OCORE-155) by @Piedone in #15695
  • Update Fluid 2.9.0 by @hishamco in #15812
  • Revising contribution docs (Lombiq Technologies: OCORE-154) by @Piedone in #15706
  • Welcome first-time contributors (Lombiq Technologies: OCORE-154) by @Piedone in #15708
  • Rename Identifier property to usernameOrEmail for clearity by @MikeAlhayek in #15814
  • add sobotama as a contributor for code by @allcontributors in #15819
  • Validate JavaScript condition in Layer Rule on save (#12901) by @sobotama in #12968
  • Add GraphQL support to UserPickerField by @hyzx86 in #15389
  • Adding Azure and AWS-specific Image Caches of ImageSharp (Lombiq Technologies: OCORE-136) by @Piedone in #15028
  • Update Microsoft.Identity.Web 2.18.0 by @hishamco in #15821
  • Update SixLabors.ImageSharp.Web.Providers.AWS & SixLabors.ImageSharp.Web.Providers.Azure 3.1.2 by @hishamco in #15822
  • Merge release/1.8.3 to main by @Piedone in #15829
  • Added 'view' permissions for media folders. by @gvkries in #15173
  • Export specific workflow by @hyzx86 in #11939
  • More automatic documentation build checks (Lombiq Technologies: OCORE-164) by @Piedone in #15887
  • Update xUnit 2.8.0 & xunit.analyzers 1.13.0 by @hishamco in #15911
  • Autoroute handle prefix as constant by @hishamco in #15918
  • Revert "Remove redirect to index after settings update" by @MikeAlhayek in #15925
  • Move UserType to OC.Users.Core by @hishamco in #15926
  • Update OpenIddict 5.5.0 by @hishamco in #15910
  • Remove leftover issue_metrics workflow (Lombiq Technologies: OCORE-166) by @Piedone in #15932
  • Update Jint 3.1.1 by @hishamco in #15912
  • Prevent AliasPart index from throwing a null exception by @MikeAlhayek in #15934
  • Reduce allocations in ShellScope by @sebastienros in #15937
  • Add a way to queue releasing shell context from setting display drivers by @MikeAlhayek in #15875
  • Use dotnet ForEachAsync implementation by @sebastienros in #15936
  • Replace IDataMigrationManager.UpdateAsync(string) with IDataMigrationManager.UpdateAsync(IEnumerable) by @hishamco in #15909
  • Convert v1.9 to v2.0 by @MikeAlhayek in #15938
  • Remove unnecessary async code by @sebastienros in #15941
  • Expose media field attribute media text in GraphQL query by @rpedu in #15945
  • Use CultureInfo.GetCultureInfo() whenever it's possible by @hishamco in #15948
  • Update NLog.Web.AspNetCore 5.3.10 by @hishamco in #15947
  • add rpedu as a contributor for code by @allcontributors in #15949
  • Fix eagerly executed task in CustomSettingService by @sebastienros in #15942
  • Cleanup Migrations by @MikeAlhayek in #15933
  • Fix docs broken logo and redirect from old URLs (Lombiq Technologies: OCORE-165) by @Piedone in #15927
  • Update release publishing guide and move it to an issue template (Lombiq Technologies: OCORE-163) by @Piedone in #15830
  • Fix creating Culture and LocalizationSet by @M-Lipin in #15714
  • Run Functional Tests - All Databases automatically (Lombiq Technologies: OCORE-167) by @Piedone in #15951
  • Upgrade YesSQL 5.0 by @MikeAlhayek in #15957
  • load flowpart and bagpart resources by name by @giannik in #15954
  • Update Workflow Type by @MikeAlhayek in #15953
  • Remove Obsolete APIs by @MikeAlhayek in #15939
  • Add EnableThreadSafetyChecks support and Enable it for all test by @MikeAlhayek in #15961
  • Running Functional Tests - All Databases on the main branch too (Lombiq Technologies: OCORE-167) by @Piedone in #15956
  • Update libphonenumber-csharp 8.13.36 by @hishamco in #15972
  • Adding a backport workflow by @MikeAlhayek in #15971
  • OpennIddict.Core 5.5.0 by @agriffard in #15991
  • Fixed an issue where feature profile expressions could not be serialized. by @TonyWoo in #15976
  • add TonyWoo as a contributor for code by @allcontributors in #15993
  • Adding Content Item ApiController.cs endpoints documentation by @MarGraz in #15512
  • Fix the search admin menu item by @MikeAlhayek in #16007
  • Update Microsoft.Identity.Web 2.18.1 by @hishamco in #16003
  • Update NLog.Web.AspNetCore 5.3.11 by @hishamco in #16011
  • Update Azure.Identity 1.11.3 by @hishamco in #16009
  • Replace Irony.Core deprecated package by @hishamco in #16012
  • Add null check in AutorouteEntries by @MikeAlhayek in #16020
  • Add type checking when importing types from recipes #15979 by @hyzx86 in #15980
  • Update the sort logic in the assets grouping by @MikeAlhayek in #15815
  • Check if element type is as requested by @tropcicstefan in #15974
  • Set up CodeRabbit for AI code reviews (Lombiq Technologies: OCORE-159) by @Piedone in #15813
  • Adding Autocomplete to Stereotype by @hyzx86 in #16025
  • Remove Newtonsoft.Json entry from docs libraries by @hishamco in #16034
  • Update 2.0.0.md by @gvkries in #16015
  • "Pleaes" typo in Close stale PRs workflow (Lombiq Technologies: OCORE-168) by @Piedone in #16045
  • ListQueryObjectType should be filtered from the database by @hyzx86 in #16037
  • Update JsonPath.Net 1.0.3 by @hishamco in #16032
  • Implement more conversions from JsonDynamicValue by @gvkries in #15816
  • Fixes comparing content fields in Liquid by @gvkries in #16053
  • Bump OpenIddict to 5.6.0 by @kevinchalet in #16054
  • .NET 8.0.5 by @agriffard in #16058
  • Support deserializing string to numeric values by @MikeAlhayek in #16060
  • Benchmarking documentation (Lombiq Technologies: OCORE-170) by @Piedone in #16061
  • Remove unnecessary placement rules from OrchardCore.Content by @giannik in #16029
  • Update Microsoft.Extensions.Http.Resilience 8.5.0 by @hishamco in #16062
  • Update Azure.Storage.Blobs 12.20.0 by @hishamco in #16063
  • Update OrchardCore.OpenId to explicitly reference Microsoft.IdentityModel.Protocols.OpenIdConnect by @kevinchalet in #16057
  • Update ContentItemVersionId nullable in graphql by @hyzx86 in #16030
  • Register a default token provider to allow admin to rest password. by @MikeAlhayek in #16075
  • Clear SiteSettings Cache after the update by @MikeAlhayek in #16068
  • Remove the ReturnUrl from the user's profile link by @MikeAlhayek in #16071
  • Add a way to specify username to send notifications to by @MikeAlhayek in #16073
  • Update Fluid 2.10.0 by @hishamco in #16086
  • Fix Azure Search AI docs by @MikeAlhayek in #16085
  • Update JsonPath.Net 1.0.4 by @hishamco in #16089
  • Update libphonenumber-csharp 8.13.37 by @hishamco in #16090
  • Update MailKit & MimeKit 4.6.0 by @hishamco in #16087
  • Fix template preview by @hishamco in #16096
  • OC project templates should show net8.0 only by @hishamco in #16048
  • Make YesSqlOptions configurable from configuration provider by @MikeAlhayek in #16079
  • Add ISmtpService extension methods by @hishamco in #14772
  • Fix AzureAI Search issues by @MikeAlhayek in #16093
  • Remove warnings related to CA1805 by @lahma in #16100
  • Fix show password button on the login screen (Lombiq Technologies: OCORE-172) by @Piedone in #16098
  • Remove warnings related to CA1707 by @lahma in #16101
  • BagPart/FlowPart editors couldn't be collapsed by @microposmp in #16099
  • Add LinkedIn & Discord links to README.md by @hishamco in #16113
  • Update JsonPath.Net 1.0.5 by @hishamco in #16115
  • Update Microsoft.Identity.Web 2.18.2 by @hishamco in #16131
  • Update JsonPath.Net 1.1.0 by @hishamco in #16132
  • Improvements for Search Azure AI by @MikeAlhayek in #16127
  • Cleanup IContentManager by @MikeAlhayek in #16077
  • Update Microsoft.NET.Test.Sdk 17.10.0 by @hishamco in #16142
  • Register GraphQL types as transient by @gvkries in #16143
  • Fix casting during workflow instance restart by @MikeAlhayek in #16139
  • Fix distributed cache document invalidation by @mvarblow in #16147
  • Issue management docs, auto-close and triage comment workflow (Lombiq Technologies: OCORE-161) by @Piedone in #15820
  • Corrects the mapping of types to features in ITypeFeatureProvider by @gvkries in #15793
  • Show recipe error instead if throwing an exeption by @MikeAlhayek in #16148
  • Fix unreliable unit test by @gvkries in #16145
  • Update Jint 3.1.2 by @hishamco in #16157
  • Update xunit 2.8.1 by @hishamco in #16158
  • Remove extra comma by @hishamco in #16159
  • Do not expose unsupported queries in the GraphQL schema. by @gvkries in #16134
  • Fix SixLabors.ImageSharp.Web reference in 1.8.3 release notes (Lombiq Technologies: OCORE-173) by @Piedone in #16150
  • Correctly escape public media URLs. by @gvkries in #16135
  • Use MinimalAPI for two-factor authentication code request by @MikeAlhayek in #16174
  • Use MinimalAPI for marking notifications as read by @MikeAlhayek in #16175
  • Prevent Functional Tests - All Databases triggering on PR comments (Lombiq Technologies: OCORE-171) by @Piedone in #16097
  • Allow any user to manage two-factor by @MikeAlhayek in #16130
  • Release packages when a preview workflow is manually triggered by @MikeAlhayek in #16183
  • Mark duplicate permissions as Obsolete by @MikeAlhayek in #16176
  • .NET 8.0.6 by @agriffard in #16192
  • Make IExternalLoginEventHandler support update User‘s properties by @hyzx86 in #12845
  • Add helpful ShellFeaturesManager extensions by @MikeAlhayek in #16191
  • Simplify error handling in recipe execution by @MikeAlhayek in #16195
  • ControllerTypeExtensions should look for ControllerBase type by @Skrypt in #16187
  • Use TextArea in MarkdownField By Default by @MikeAlhayek in #16189
  • Simplifies creating child containers for tenants. by @gvkries in #16190
  • Update Microsoft.Identity.Web 2.19.0 by @hishamco in #16199
  • Add helpful SiteService extension by @MikeAlhayek in #16193
  • Fixes building the GraphQL schema. by @gvkries in #16184
  • Remove old non-async Alter* content definition method references from the docs and templates (Lombiq Technologies: OCORE-177) by @Piedone in #16228
  • Configure Https Async by @MikeAlhayek in #16222
  • Add supported_cultures liquid filter by @MikeAlhayek in #16208
  • Move ContentCulturePicker logic back to driver for easier shape override (Lombiq Technologies. OCORE-178) by @Piedone in #16230
  • Correct the order of module configurations by @MikeAlhayek in #16227
  • Add example for adding basic content type filters by @MikeKry in #16111
  • Make SQL query source more forgiving for returning content items by @gvkries in #16234
  • fix serialization issue with stj by @giannik in #16236
  • Constrain the paths created DefaultAreaControllerRouteMapper with and AdminAreaControllerRouteMapper (Lombiq Technologies: OCORE-176) by @sarahelsaig in #16217
  • Add SiteSettings for custom settings and to get settings by name by @MikeAlhayek in #16220
  • Fix ListNotificationOptions assignment by @hishamco in #16239
  • Fix JsonDynamicArray enumeration in dynamic usages by @gvkries in #16246
  • Fixes the content items admin menu when types are not creatable by @gvkries in #16247
  • Remove duplicate condition in LinkFieldIndexProvider.Describe() by @hishamco in #16251
  • Avoid NRE in SharedViewCompilerProvider.GetCompiler() by @hishamco in #16252
  • Add missing version assignment in StyleBlock by @hishamco in #16241
  • Seal all Startup, Configuration and Filter classes by @MikeAlhayek in #16238
  • Seal all migration and background tasks classes by @MikeAlhayek in #16253
  • Fix Content-Types and Content-Parts search UI by @MikeAlhayek in #16237
  • Update libphonenumber-csharp 8.13.38 by @hishamco in #16255
  • Add target attribute to Menu by @hyzx86 in #15636
  • Fix missing activity display driver by @NinjaChurch in #13947
  • Remove unecessary Convert.ToBoolean() in RuleBenchmark by @hishamco in #16240
  • Remove duplicate unit tests by @hishamco in #16250
  • Remove unnecessary RequireSettings.Condition check in ResourceRequiredContext by @hishamco in #16242
  • Bug report and issue management improvements (Lombiq Technologies: OCORE-174) by @Piedone in #16206
  • Auto-close old community metrics issue(s) (Lombiq Technologies: OCORE-175) by @Piedone in #16207
  • Makes sure data migrations are assigned to a single feature only by @gvkries in #16257
  • Fix that Comment Issue on Triage runs for PR milestone changes too (Lombiq Technologies: OCORE-179) by @Piedone in #16261
  • Fixes several minor issues when building the GraphQL schema by @gvkries in #16151
  • Add option to set CORS exposed headers by @vsezima in #16258
  • add vsezima as a contributor for code by @allcontributors in #16262
  • Make all extensions/Helpers classes static and seal some classes by @MikeAlhayek in #16264
  • Don't Serialize in GetValue<> of Dictionary Extensions by @MikeAlhayek in #16224
  • Migrate to Central Package Management by @tmat in #16235
  • add tmat as a contributor for code by @allcontributors in #16268
  • Fix solution build file references (Lombiq Technologies: OCORE-180) by @Piedone in #16269
  • Remove Gitter references (Lombiq Technologies: OCORE-181) by @Piedone in #16270
  • Add serialisation compatibility of TimeSpan and DateTime by @hyzx86 in #16205
  • Update Azure.Identity 1.11.4 by @hishamco in #16286
  • Update NuGet references automatically once a week by @sebastienros in #16284
  • Fixes menu links in the blog theme by @gvkries in #16312
  • Fix possible NREs in SeoMetaSettingsHandler by @gvkries in #16311
  • Remove npm assets from dependabot by @sebastienros in #16305
  • Fix comments to clarify that an absolute path does not work for OrchardCore_Media__AssetsPaths by @rjpowers10 in #16293
  • Bump the all-dependencies group across 1 directory with 8 updates by @dependabot in #16319
  • Fixing that saving workflow fails when using decimal comma (,) by @microposmp in #16274
  • Remove unneeded AWSSDK.SecurityToken package reference (Lombiq Technologies: OCORE-185) by @Piedone in #16320
  • Expose Content Fields Indexing via GraphQL for content field filtering by @mdameer in #16092
  • add mdameer as a contributor for code by @allcontributors in #16327
  • Remove unnecessary check in ScriptTag by @hishamco in #16328
  • Added option to select the Target in Link Field by @vengi83644 in #16326
  • Remove unnecessary cast by @hishamco in #16333
  • Fix Azure Email docs page JSON code block (Lombiq Technologies: OCORE-186) by @Piedone in #16334
  • Check HttpContext in Facebook ScriptsMiddleware before use it by @hishamco in #16331
  • Prevent NuGet audit warnings from failing the CI builds (Lombiq Technologies: OCORE-184) by @Piedone in #16317
  • Remove unnecessary null coalescing operator by @hishamco in #16332
  • Adds an overload to RegisterBeforeDispose() by @gvkries in #16322
  • Avoid auto complete for OpenID scopes by @hishamco in #16160
  • Update navigation links with target element (Lombiq Technologies: OCORE-187) by @sarahelsaig in #16341
  • Bump the all-dependencies group with 3 updates by @dependabot in #16335
  • Fix Facebook widgets recipe migration (Lombiq Technologies: OCORE-188) by @sarahelsaig in #16347
  • Fix Twitter module breaking the Workflows editor (Lombiq Technologies: OCORE-189) by @sarahelsaig in #16350
  • Liquid syntax support for CorrelateTask by @lampersky in #12021
  • Implement a default service to link local user accounts with external ones during the registration process by @PiemP in #16110
  • Correction of a clerical error by @hyzx86 in #16367
  • Adds more features back that were lost due to the removal of Newtonsoft by @gvkries in #16292
  • Fix Wysiwyg/Trumbowyg editor colors when dark theme enabled by @mdameer in #16356
  • Fixes that type/feature mappings are not available during setup by @gvkries in #16324
  • Fix for case when src is not provided by @SzymonSel in #16368
  • Fix JsonDynamic related object serialisation problem in Jint. by @hyzx86 in #16298
  • Style blockquote in markdown editor by @MikeAlhayek in #16390
  • Remove extra space in first_time_contributor.yml by @hishamco in #16394
  • Hide "Number of items closed" from Discussion Metrics (Lombiq Technologies: OCORE-183) by @Piedone in #16315
  • Improving community GitHub Actions workflows (Lombiq Technologies: OCORE-182) by @Piedone in #16295
  • Bump the all-dependencies group with 10 updates by @dependabot in #16365
  • Fixes missing dependencies in the ShellBlueprint by @gvkries in #16379
  • Remove 'Target' from LinkFieldIndex by @MikeAlhayek in #16397
  • Fix theme manager script by @MikeAlhayek in #16386
  • Added unit tests for populating the type feature provider by @gvkries in #16399
  • Remove set as startup task button for non-event activities by @mdameer in #16351
  • Fix graphql where clause when database schema is configured by @mdameer in #16364
  • Fix non-debug version loaded for certain static resources when debug mode is enabled by @mdameer in #16406
  • Bump the all-dependencies group with 7 updates by @dependabot in #16407
  • .NET 8.0.7 by @agriffard in #16413
  • Deserialize compressed documents correctly by @MikeAlhayek in #16415
  • Declare known-converters and use them in DocumentJsonSerializerOptions by @MikeAlhayek in #16417
  • Fix Elasticsearch Query API by @MikeAlhayek in #16424
  • Fix ExpandoObject property access by @MikeAlhayek in #16432
  • Fix Sitemap Import/Export by @MikeAlhayek in #16436
  • Removing invalid [FromForm] from TenantApiController.Setup (Lombiq Technologies: OCORE-191) by @Piedone in #16439
  • Fixing async voids (Lombiq Technologies: OCORE-192) by @Piedone in #16441
  • Fix lost ReturnUrl param after changing localization by @maksim-markin in #16325
  • add maksim-markin as a contributor for code by @allcontributors in #16443
  • Removes a reference to options in RequireSettings by @gvkries in #16451
  • Fix footer in darkmode (Lombiq Technologies: NEST-515) by @Psichorex in #16444
  • Fixes that the media library uses the wrong file extensions by @gvkries in #16449
  • Use Semaphore instead of lock in DefaultShapeTableManager by @MikeAlhayek in #16456
  • Suppress Check For Unhandled Security Metadata by @MikeAlhayek in #16437
  • Font awesome 6.6.0 by @agriffard in #16455
  • Fix the QueriesDocument and the Queries UI by @MikeAlhayek in #16402
  • Add new helpful Entity Extensions by @MikeAlhayek in #16457
  • Minor cleanup on 2.0.0 release notes by @MikeAlhayek in #16462
  • Seal and cleanup all recipe handlers by @MikeAlhayek in #16464
  • Correct zero length check expressions by @MikeAlhayek in #16463
  • Update MimeKit to 4.7.1 - High Vulnerability by @MikeAlhayek in #16472
  • Update Microsoft.Extensions.Http.Resilience by @MikeAlhayek in #16469
  • Update Jint by @MikeAlhayek in #16471
  • Setup with saved password by @sarahelsaig in #16466
  • Bump the all-dependencies group with 16 updates by @dependabot in #16474
  • Update Fluid.Core, YesSQL, and Shortcodes by @MikeAlhayek in #16470
  • OpenId UI Enhancements by @MikeAlhayek in #16467
  • Small optimizations for the ExtensionManager by @gvkries in #16382
  • Make it possible to add Razor HTML content to the shape as a property by @sarahelsaig in #14867
  • Make ResourcesTagHelper extensible by @sarahelsaig in #16329
  • Handle invalid recipe files from breaking the harvester by @MikeAlhayek in #16490
  • Add RemovePart extension by @MikeAlhayek in #16491
  • Move Media Permissions to Media.Core by @MikeAlhayek in #16493
  • fix for "can't add Part when Named Part is already added" by @lampersky in #16495
  • Add CanHandleModelAsync by @MikeAlhayek in #16501
  • Bump the all-dependencies group with 11 updates by @dependabot in #16503
  • Additional warning about <resources type="..." /> in NuGet. by @sarahelsaig in #16507
  • Keep only important virtual methods in drivers by @MikeAlhayek in #16508
  • Bump the all-dependencies group with 5 updates by @dependabot in #16519
  • Renaming Query should not clone by @MikeAlhayek in #16527
  • Clean up the SectionDisplayDriverBase and seal more methods in SiteDisplayDriver by @MikeAlhayek in #16528
  • Add AddTenantReloadWarningWrapper() helpful extension by @MikeAlhayek in #16532
  • Seal User Events Display Drivers by @MikeAlhayek in #16536
  • Seal all display drivers by @MikeAlhayek in #16535
  • Fix Taxonomy Term Removal by @MikeAlhayek in #16538
  • Workflow pruning task by @deanmarcussen in #16533
  • Keep only important methods in ContentPartDisplayDriver and ContentFieldDisplayDriver by @MikeAlhayek in #16530
  • Add Sortable dependency to script using Sortable by @MikeAlhayek in #16531
  • PoParser PERF by @hishamco in #15943
  • Add Blazor guide for decoupled cms. by @ns8482e in #15213
  • Check model object in CreateEndpoint to avoid NRE by @hishamco in #16243
  • Improved MediaField Graphql support by @hyzx86 in #15003
  • Disable autocomplete for password fields by @MikeAlhayek in #16544
  • Disable old media field GraphQL fields by default. by @gvkries in #16546
  • Bump the all-dependencies group with 7 updates by @dependabot in #16549
  • .NET 8.0.8 by @agriffard in #16550
  • Convert to file-scope namespaces by @MikeAlhayek in #16539
  • Add .git-blame-ignore-revs file by @MikeAlhayek in #16559
  • Fix wrong autocomplete=new-password on the current password field. by @gvkries in #16558
  • Add HttpContextExtensions for GetActionContextAsync by @infofromca in #16557
  • Fire the login failed event only once when a known user login fails. by @mvarblow in #16552
  • Additional logs for ElasticSearch failures by @AndreySurkov in #16564
  • Return services from workflow registration extension methods by @deanmarcussen in #16568
  • Fix localization rule \Localization[CultureName][ModuleId].po by @hyzx86 in #16419
  • Add Message template to The Theme and unify font-awesome injection by @MikeAlhayek in #16569
  • Create global.json file by @sebastienros in #16560
  • Define formatting rules by @sebastienros in #16567
  • Use transitive pinning and update dependencies by @sebastienros in #16566
  • Throws an exception if the datetime cannot be converted. by @hyzx86 in #16288
  • Remove Index post action in Layers by @MikeAlhayek in #16571
  • Workflow trimming (pruning) improvements by @Piedone in #16565
  • Refactorings in preparation of .NET 9.0 support by @sebastienros in #16575
  • Enable implicit usings by @MikeAlhayek in #16574
  • Seal all controllers by @MikeAlhayek in #16576
  • Fix SQL document queries don't work on older existing tenants (Lombiq Technologies: OCORE-195) by @sarahelsaig in #16581
  • Add UserConfirmed workflow event for user email confirmation by @davidpuplava in #16584
  • add davidpuplava as a contributor for code by @allcontributors in #16591
  • Fix Json Merge object by @MikeAlhayek in #16589
  • Add permission check in edit WorkflowTrimmingDisplayDriver by @hishamco in #16593
  • Rename Twitter/X workflow task to match updated view names by @davidpuplava in #15930
  • Do not include "Change password" item in the user menu if local login is disabled by @mvarblow in #16598
  • Add a perf-importent Notification index by @MikeAlhayek in #16590
  • Fix user menu text alignment by @mvarblow in #16596
  • Fix invalid HTML (Lombiq Technologies: OCORE-196) by @sarahelsaig in #16607
  • Add a base document-index to allow indexing non-content items in AzureAI search by @MikeAlhayek in #16611
  • Add AddNavigationProvider extension by @MikeAlhayek in #16618
  • Fix User Registration by @MikeAlhayek in #16621
  • Add AddSiteDisplayDriver extension by @MikeAlhayek in #16617
  • Add AddPermissionProvider extension by @MikeAlhayek in #16622
  • Fix hot reload issue. by @shinexyt in #16624
  • add shinexyt as a contributor for code by @allcontributors in #16626
  • Fix formatting by @sebastienros in #16627
  • Fix case insensitive dictionary problem (Lombiq Technologies: OCORE-197) by @sarahelsaig in #16623
  • Correct the configuration of JsonSerializerOptions by @MikeAlhayek in #16634
  • fix validation error rendering on radio/checkbox by @ns8482e in #16637
  • Fix comment in DocumentJsonSerializerOptionsConfiguration by @gvkries in #16639
  • Use const when possible by @MikeAlhayek in #16635
  • Optimizing Sitemap Creation with Batched Content Items by @MikeAlhayek in #16636
  • Fix driver results initialization method usage with dynamic caching by @sebastienros in #16644
  • Fix MissingActivity view by @MikeAlhayek in #16642
  • Cache Top-unread notification in the navar for better performance. by @MikeAlhayek in #16641
  • Remove duplicate ManagePackageVersionsCentrally by @MikeAlhayek in #16647
  • Add .git-blame-ignore-revs file by @sebastienros in #16648
  • Add IUserTimeZoneService to make it easier to mock UserTimeZoneService by @hishamco in #16614
  • Use the static 'ValueTask.FromResult' instead of 'new ValueTask' by @MikeAlhayek in #16659
  • Use ValueTask in INavigationProvider by @MikeAlhayek in #16650
  • Bump webpack from 5.88.2 to 5.94.0 in /src/OrchardCore.Modules/OrchardCore.AuditTrail by @dependabot in #16640
  • Bump webpack from 5.88.2 to 5.94.0 in /src/OrchardCore.Modules/OrchardCore.Apis.GraphQL by @dependabot in #16660
  • Remove tenant list from OpenID scopes by @MikeAlhayek in #16651
  • Add default OpenId scopes by @MikeAlhayek in #16661
  • Add "Create & Setup" button to the create tenant view by @MikeAlhayek in #16665
  • Use Expiry-sliding in notification cache by @MikeAlhayek in #16662
  • Merge Release 1.8.4 into main by @MikeAlhayek in #16668
  • Revert driver initialization logic and adapt Notifications cache by @sebastienros in #16677
  • Add missing await keywork in ShapeResult by @sebastienros in #16683
  • Add AdminNavigationProvider to cleanup AdminMenu by @MikeAlhayek in #16674
  • Use ArgumentNullException.ThrowIfNull() by @hishamco in #16684
  • Support implicit casts from JsonDynamicValue. by @gvkries in #16696
  • Ensure correct feature is enabled AAD by @giannik in #16694

New Contributors

  • @thatplatypus made their first contribution in #14611
  • @ovekaaven made their first contribution in #15084
  • @porgabi made their first contribution in #14795
  • @Psichorex made their first contribution in #15183
  • @AndreySurkov made their first contribution in #15239
  • @SimonCropp made their first contribution in #15244
  • @MarGraz made their first contribution in #15265
  • @M-Lipin made their first contribution in #15495
  • @w-ko made their first contribution in #15392
  • @aliamiras made their first contribution in #15560
  • @xtomas made their first contribution in #15545
  • @rwawr made their first contribution in #15603
  • @sobotama made their first contribution in #12968
  • @rpedu made their first contribution in #15945
  • @TonyWoo made their first contribution in #15976
  • @NinjaChurch made their first contribution in #13947
  • @vsezima made their first contribution in #16258
  • @tmat made their first contribution in #16235
  • @mdameer made their first contribution in #16092
  • @maksim-markin made their first contribution in #16325
  • @davidpuplava made their first contribution in #16584
  • @shinexyt made their first contribution in #16624

Full Changelog: https://github.com/OrchardCMS/OrchardCore/compare/v1.8.4...v2.0.0

Important Upgrade Instructions

Prior to making the leap to Orchard Core 2.0, it's crucial to follow a step-by-step upgrade process. Begin by upgrading your current version to 1.8, resolving any obsolete warnings encountered along the way, and guaranteeing that every site successfully operates on version 1.8.3. This approach ensures that all sites are seamlessly transitioned to 1.8. Subsequently, proceed with confidence to upgrade to 2.0. Prior to initiating this upgrade, thoroughly review the documented list of breaking changes provided below to facilitate a smooth and hassle-free transition.

Breaking Changes

Drop Newtonsoft.Json Support

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 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>();

Alternatively, you can simplify your code by using the newly added extensions to register custom conditions. For example,

services.AddRule<HomepageCondition, HomepageConditionEvaluator, HomepageConditionDisplayDriver>();
  • Any type introduced in custom modules inheriting from MenuItem, AdminNode, Condition, ConditionOperator, Query, SitemapType will have to register the class using the services.AddJsonDerivedTypeInfo<> method. For example,
services.AddJsonDerivedTypeInfo<SqlQuery, Query>();
  • The extension PopulateSettings<T>(model) was removed from PartFieldDefinition. If you are using this method in your code, you'll have to get the settings using the Settings object directly. For instance, if you have this code,
public override IDisplayResult Edit(ContentPartFieldDefinition partFieldDefinition)
{
   return Initialize<NumericFieldSettings>("NumericFieldSettings_Edit", model => partFieldDefinition.PopulateSettings(model));
}

You'll change it to the following:

 public override IDisplayResult Edit(ContentPartFieldDefinition partFieldDefinition)
{
   return Initialize<NumericFieldSettings>("NumericFieldSettings_Edit", model => 
   {
       var settings = partFieldDefinition.GetSettings<NumericFieldSettings>();
       model.Hint = settings.Hint;
       // ...
   });
}

Queries

Previously, any query type had to inherit from Query and required its own distinct type (e.g., SqlQuery, LuceneQuery, ElasticQuery). However, with pull request #16402, creating a custom type for each query type is no longer necessary. This update involved changes to the IQueryManager and IQuerySource interfaces, as well as the Query class. Additionally, a new project, OrchardCore.Queries.Core, was introduced.

A migration process has been implemented to transition existing queries into the new structure, ensuring no impact on existing tenants.

Key Changes

  • Modification of Interfaces and Classes: Updates were made to the IQueryManager, IQuerySource interface, and Query class to accommodate the new structure.
  • Addition of OrchardCore.Queries.Core: This new project supports the updated query handling mechanism.

Implementing IQuerySource

We now request implementations of IQuerySource using keyed services. Below is an example of how to register new implementations:

services.AddQuerySource<SqlQuerySource>(SqlQuerySource.SourceName);

This approach allows for more flexible and modular query handling in OrchardCore.

Handlers

We now have handlers for dealing with queries. To manipulate a query using a handler, implement the IQueryHandler interface. This provides a structured way to extend and customize the behavior of queries within the framework.

Twitter Module

The Twitter module has been rebranded to X.

If you were using the OrchardCore_Twitter configuration key to configure the module, please update the configuration key to OrchardCore_X. The OrchardCore_Twitter key continues to work but will be deprecated in a future release.

Users Module

  • The Login.cshtml has undergone a significant revamp. The previous AfterLogin zone, which allowed filters for injecting shapes, has been replaced. Now, you can inject shapes using drivers by implementing IDisplayDriver<LoginForm>. For example, the 'Forgot Password?' link is injected using the following driver:
public class ForgotPasswordLoginFormDisplayDriver : DisplayDriver<LoginForm>
{
    private readonly ISiteService _siteService;

    public ForgotPasswordLoginFormDisplayDriver(ISiteService siteService)
    {
        _siteService = siteService;
    }

    public override async Task<IDisplayResult> EditAsync(LoginForm model, BuildEditorContext context)
    {
        var settings = await _siteService.GetSettingsAsync<ResetPasswordSettings>();

        if (!settings.AllowResetPassword)
        {
            return null;
        }

        return View("LoginFormForgotPassword_Edit", model).Location("Links:5");
    }
}
  • The ForgotPassword.cshtml has undergone a significant revamp. The previous AfterForgotPassword zone, which allowed filters for injecting shapes, has been replaced. Now, you can inject shapes using drivers by implementing IDisplayDriver<ForgotPasswordForm>. For example, the ReCaptcha shape is injected using the following driver:
public class ReCaptchaForgotPasswordFormDisplayDriver : DisplayDriver<ForgotPasswordForm>
{
    private readonly ISiteService _siteService;

    public ReCaptchaForgotPasswordFormDisplayDriver(ISiteService siteService)
    {
        _siteService = siteService;
    }

    public override async Task<IDisplayResult> EditAsync(ForgotPasswordForm model, BuildEditorContext context)
    {
        var settings = await _siteService.GetSettingsAsync<ReCaptchaSettings>();

        if (!settings.IsValid())
        {
            return null;
        }

        return View("FormReCaptcha_Edit", model).Location("Content:after");
    }
}
  • The ResetPassword.cshtml has undergone a significant revamp. The previous AfterResetPassword zone, which allowed filters for injecting shapes, has been replaced. Now, you can inject shapes using drivers by implementing IDisplayDriver<ResetPasswordForm>. For example, the ReCaptcha shape is injected using the following driver:
public class ReCaptchaResetPasswordFormDisplayDriver : DisplayDriver<ResetPasswordForm>
{
    private readonly ISiteService _siteService;

    public ReCaptchaResetPasswordFormDisplayDriver(ISiteService siteService)
    {
        _siteService = siteService;
    }

    public override async Task<IDisplayResult> EditAsync(ResetPasswordForm model, BuildEditorContext context)
    {
        var settings = await _siteService.GetSettingsAsync<ReCaptchaSettings>();

        if (!settings.IsValid())
        {
            return null;
        }

        return View("FormReCaptcha_Edit", model).Location("Content:after");
    }
}

Previously, users were only able to reset their password through email when the "Reset Password" feature was enabled. However, we've enhanced this functionality to offer users the flexibility of resetting their password using either their email or username. Consequently, the Email property on both the ForgotPasswordViewModel and ResetPasswordViewModel have been deprecated and should be replaced with the new UsernameOrEmail property for password resets.

  • The Register.cshtml has undergone a significant revamp. The previous AfterRegister zone, which allowed filters for injecting shapes, has been replaced. Now, you can inject shapes using drivers by implementing IDisplayDriver<RegisterUserForm>. For example, the ReCaptcha shape is injected using the following driver:
public class RegisterUserFormDisplayDriver : DisplayDriver<RegisterUserForm>
{
    private readonly ISiteService _siteService;

    public RegisterUserFormDisplayDriver(ISiteService siteService)
    {
        _siteService = siteService;
    }

    public override async Task<IDisplayResult> EditAsync(RegisterUserForm model, BuildEditorContext context)
    {
        var settings = await _siteService.GetSettingsAsync<ReCaptchaSettings>();

        if (!settings.IsValid())
        {
            return null;
        }

        return View("FormReCaptcha_Edit", model).Location("Content:after");
    }
}
  • Two-factor authentication (2FA) is now available for all users. Previously, only users with permission to access the admin area could set up their own 2FA methods, although all users could still use 2FA if it was set up by an admin. To enable this change, the method IsRequiredAsync() in both the ITwoFactorAuthenticationHandlerCoordinator and ITwoFactorAuthenticationHandler interfaces has been updated to IsRequiredAsync(IUser user).

  • A new UserConfirmedEvent workflow event is now available. This event is triggered when a user successfully confirms their email address using the link sent during user registration.

  • We are introducing a new interface, IUserTimeZoneService, to replace the existing UserTimeZoneService. If your project directly injects UserTimeZoneService, you will need to switch to using IUserTimeZoneService instead.

Notifications Module

The endpoint for marking notifications as read has been updated. Previously the route was /api/notifications/read, and now it is /Notifications/MarkAsRead. This change will only affect you if you have overridden the UserNotificationNavbar view.

Contents

The IContentManager interface was modified. The method Task<IEnumerable<ContentItem>> GetAsync(IEnumerable<string> contentItemIds, bool latest = false) was removed. Instead use the method that accepts VersionOptions by providing either VersionOptions.Latest or VersionOptions.Published will be used by default.

Additionally, the GetContentItemByHandleAsync(string handle, bool latest = false) and GetContentItemByIdAsync(string contentItemId, bool latest = false) were removed from IOrchardHelper. Instead use the method that accepts VersionOptions by providing either VersionOptions.Latest or VersionOptions.Published will be used by default.

Lastly, we dropped support for AllVersions option in VersionOptions. Instead, you can use the new GetAllVersionsAsync(string contentItemId) method on IContentManager.

Media Indexing

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.

SMS Module

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.

Display Management

In this release, the signatures of the UpdateAsync() method within the SectionDisplayDriver base class have undergone modifications. Previously, these signatures accepted the BuildEditorContext parameter. However, with this update, all signatures now require the UpdateEditorContext instead. This alteration necessitates that every driver inheriting from this class adjusts their contexts accordingly.

Here are the updated signatures:

  1. From: Task<IDisplayResult> UpdateAsync(TModel model, BuildEditorContext context) To: Task<IDisplayResult> UpdateAsync(TModel model, UpdateEditorContext context)

  2. From: Task<IDisplayResult> UpdateAsync(TModel model, TSection section, BuildEditorContext context) To: Task<IDisplayResult> UpdateAsync(TModel model, TSection section, UpdateEditorContext context)

  3. From: Task<IDisplayResult> UpdateAsync(TSection section, BuildEditorContext context) To: Task<IDisplayResult> UpdateAsync(TSection section, UpdateEditorContext context). This method is now obsolete.

These adjustments ensure compatibility and adherence to the latest conventions within the DisplayDriver class.

Additionally, we've enhanced display performance by streamlining the display drivers. To achieve this, the following methods have been marked as obsolete:

  • IDisplayResult Display(TModel model)
  • IDisplayResult Edit(TModel model)

and removed the following methods:

  • Task<IDisplayResult> DisplayAsync(TModel model, IUpdateModel updater)
  • IDisplayResult Display(TModel model, IUpdateModel updater)
  • Task<IDisplayResult> UpdateAsync(TModel model, IUpdateModel updater)
  • Task<IDisplayResult> EditAsync(TModel model, IUpdateModel updater)
  • IDisplayResult Edit(TModel model, IUpdateModel updater)

At the same time, we've introduced the following static methods in the DisplayDriverBase:

  • Task<IDisplayResult> CombineAsync(params IDisplayResult[] results)
  • Task<IDisplayResult> CombineAsync(IEnumerable<IDisplayResult> results)

Similar refactoring was done to SectionDisplayDriver class. The following methods have been marked as obsolete:

  • Display(TSection section, BuildDisplayContext context)
  • Display(TSection section)
  • Edit(TSection section, BuildEditorContext context)
  • UpdateAsync(TSection section, UpdateEditorContext context)
  • UpdateAsync(TSection section, IUpdateModel updater, string groupId)

and removed the following methods:

  • UpdateAsync(TModel model, TSection section, IUpdateModel updater, UpdateEditorContext context)
  • UpdateAsync(TSection section, IUpdateModel updater, UpdateEditorContext context)
  • UpdateAsync(TSection section, IUpdateModel updater, string groupId)
  • UpdateAsync(TSection section, UpdateEditorContext context)
  • EditAsync(TSection section, BuildEditorContext context)
  • DisplayAsync(TSection section, BuildDisplayContext context)

!!! note To enhance your project's performance, address any warnings generated by the use of these obsolete methods. Please note that these methods will be removed in the next major release.

A new SiteDisplayDriver base class was introduced to simplify the process of adding a UI settings.

!!! note With the new SiteDisplayDriver base class for custom settings, you no longer need explicit checks like context.GroupId.Equals(GroupId, StringComparison.OrdinalIgnoreCase) as the base class handles this check for you.

A new extension has been introduced to simplify the registration of site setting drivers. Instead of using service.AddScoped<IDisplayDriver<ISite>, CustomSettingsDisplayDriver>(), you can now register it with the more streamlined service.AddSiteDisplayDriver<CustomSettingsDisplayDriver>().

Similarly, the ContentPartDisplayDriver base class have undergone modifications. The following methods were marked obsolete:

  • IDisplayResult Display(TPart part)
  • IDisplayResult Edit(TPart part)
  • Task<IDisplayResult> UpdateAsync(TPart part, IUpdateModel updater)

At the same time, the following method was removed:

  • Task<IDisplayResult> UpdateAsync(TPart part, IUpdateModel updater)

Moreover, the ContentFieldDisplayDriver base class have undergone modifications. The following methods were marked obsolete:

  • UpdateAsync(TField field, IUpdateModel updater, UpdateFieldEditorContext context)
  • Update(TField field, IUpdateModel updater, UpdateFieldEditorContext context)

At the same time, the following method was added:

  • UpdateAsync(TField field, UpdateFieldEditorContext context)

GraphQL Module

The GraphQL schema may change because fields are now always added to the correct part. Previously, additional fields may have been added to the parent content item type directly.

You may have to adjust your GraphQL queries in that case.

Additionally, the GetAliases method in the IIndexAliasProvider interface is now asynchronous and has been renamed to GetAliasesAsync. Implementations of this interface should be modified by updating the method signature and ensure they handle asynchronous operations correctly.

Media fields

The schema for media fields has been updated. Instead of separate lists for fileNames, urls, paths, and mediatexts, these fields are now grouped under a single files type to simplify data binding on the front end.

Please update your existing queries as shown in the following example:

Before:

{
  article {
    contentItemId
    image {
      mediatexts(first: 10)
      urls(first: 10)
    }
  }
}

After:

{
  article {
    contentItemId
    image {
      files(first: 10) {
        mediaText
        url
      }
    }
  }
}

A compatibility switch has been introduced to allow continued use of the old fileNames, urls, paths, and mediatexts fields. To restore the legacy fields, add the following AppContext switch to your startup class:

AppContext.SetSwitch("OrchardCore.Media.EnableLegacyMediaFieldGraphQLFields", true);

Resource Management

Previously the <resources type="..." /> Razor tag helper and the {% resources type: "..." %} Liquid tag were only capable of handling a hard-coded set of resource definition types (script, stylesheet, etc). Now both can be extended with IResourcesTagHelperProcessor to run custom rendering logic. To make this possible, the OrchardCore.ResourceManagement.TagHelpers.ResourceType and OrchardCore.Resources.Liquid.ResourcesTag.ResourceType enums have been replaced with a common OrchardCore.ResourceManagement.ResourceTagType. It was renamed to avoid confusion with ResourceDefinition.Type. This change does not affect the uses of the Razor tag helper or the Liquid tag in templates of user projects, but it affects published releases such as NuGet packages. Any themes and modules that contain <resources type="..." /> in a Razor file (e.g. Layout.cshtml) must be re-released to generate the updated .cshtml.cs files, because the old pre-compiled templates would throw MissingMethodException.

Workflows Module

Previously, the CreateContentTask, RetrieveContentTask, and UpdateContentTask in the workflow's CorrelationId was updated each time with the ContentItemId of the current content item. Now, the CorrelationId value will only be updated if the current workflow's CorrelationId is empty.

Additionally, a new workflow-scoped script function setCorrelationId(id:string): void was added, that you can use to update the workflow's CorrelationId.

Navigations Module

The BuildNavigationAsync method in the INavigationProvider interface was optimized by changing its return type from Task to ValueTask.

Change Logs

Azure AI Search Module

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.

New ImageSharp Image Caches for Azure Blob and AWS S3 Storage

The Microsoft Azure Media and Amazon S3 Media modules have new features for replacing the default PhysicalFileSystemCache of ImageSharp that stores resized images in the local App_Data folder. Instead, you can now use Azure Blob Storage with the Azure Media ImageSharp Image Cache feature (that utilizes AzureBlobStorageImageCache), and AWS S3 with the Amazon Media ImageSharp Image Cache feature (that utilizes AWSS3StorageCache). Depending on your use case, this can provide various advantages. Check out the Azure Media and the Amazon S3 Media docs for details.

Deployment Module

New Extensions

Added new extensions to make registering custom deployment step easier:

  • services.AddDeployment<TSource, TStep>().
  • services.AddDeployment<TSource, TStep, TDisplayDriver>().
  • services.AddDeploymentWithoutSource<TStep, TDisplayDriver>().

Recipe Executions

Before this release, implementations of IRecipeStepHandler or IRecipeEventHandler would throw exceptions to report errors if something failed to import. However, this is no longer the recommended approach for error reporting.

Now, to handle errors, we have introduced a new property named Errors in the RecipeExecutionContext. This property allows you to log errors instead of throwing exceptions. These errors should be localized and must not contain any sensitive data, as they are visible to the end user. Exceptions are still used for logging additional information, but these are not shown to the end user.

Additionally, if an error occurs, a new custom exception, RecipeExecutionException, is thrown. For more info visit the related pull-request.

Workflows Module

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.

New Events

The following events were added:

  • UserConfirmedEvent: this event triggers when a user successfully confirms their email address after registration.

GraphQL Module

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 GraphQL module now allows for filtering content fields, making it easier to query specific content fields using GraphQL. This enhancement relies on having the OrchardCore.ContentFields.Indexing.SQL module enabled.

To filter content items based on a specific content field, you can use a query like this:

product(where: {price: {amount_gt: 10}}) {
    contentItemId
    displayText
    price {
        amount
    }
}

Email Module

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 inject IEmailService instead.
  • The SMTP related services are now part of a new module named OrchardCore.Email.Smtp. To use the SMTP provider for sending emails, enable the OrchardCore.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 to OrchardCore_Email_Smtp. The OrchardCore_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.

Menu

Menus and AdminMenus now support specifying the target property.

Admin Menu

The admin menu has been optimized with significant performance improvements and code enhancements. When adding a navigation provider to the admin menu, it is now recommended to use the new AdminNavigationProvider class rather than directly implementing the INavigationProvider interface in your project.

Furthermore, when passing route values to actions, it's a best practice to store these values in constant variables for maintainability and clarity. Below is an example demonstrating this approach:

public sealed class AdminMenu : AdminNavigationProvider
{
    private static readonly RouteValueDictionary _routeValues = new()
    {
        { "area", "OrchardCore.Settings" },
        { "groupId", AdminSiteSettingsDisplayDriver.GroupId },
    };

    internal readonly IStringLocalizer S;

    public AdminMenu(IStringLocalizer<AdminMenu> stringLocalizer)
    {
        S = stringLocalizer;
    }

    protected override ValueTask BuildAsync(NavigationBuilder builder)
    {
        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 ValueTask.CompletedTask;
    }
}

A new extension has been introduced to simplify the registration of navigation providers. Instead of using services.AddNavigationProvider<AdminMenu>(), you can now register it with the more streamlined services.AddNavigationProvider<AdminMenu>();.

Admin Routes

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 sealed 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).

Users Module

Added a new User Localization feature that allows to be able to configure the culture per user from the admin UI.

Navbar

Added a new Navbar() function to Liquid to allow building the Navbar shape using Liquid. Here are the steps needed to add the Navbar shape into your custom Liquid shape:

  1. Construct the shape at the beginning of the layout.liquid file to enable navbar items to potentially contribute to the resources output as necessary.
{% assign navbar = Navbar() | shape_render %}
  1. Subsequently in the layout.liquid file, invoke the shape at the location where you want to display it.
{{ navbar }}

Notifications Module

TheINotificationMessage interface was updated to includes the addition of a Subject field, which facilitates the rendering of notification titles. Moreover, the existing Summary field has been transitioned to HTML format. This adjustment enables the rendering of HTML notifications in both the navigation bar and the notification center. Consequently, HTML notifications can now be created, affording functionalities such as clickable notifications.

Furthermore, the introduction of the NotificationOptions provides configuration capabilities for the notifications module. This structure comprises the following attributes:

  • TotalUnreadNotifications: This property specifies the maximum number of unread notifications that are displayed in the navigation bar. By default, it is set to 10.
  • DisableNotificationHtmlBodySanitizer: Notifications generated from workflows have their HtmlBody sanitized by default. This property allows you to disable the sanitization process if needed.
  • AbsoluteCacheExpirationSeconds: Specifies the absolute maximum duration, in seconds, for which the top unread user notifications are cached when caching is enabled. A value of 0 does not disable caching but indicates that there is no fixed expiration time for the cache. You can set this value to define a maximum lifespan for the cached data before it is invalidated.
  • SlidingCacheExpirationSeconds: Defines the sliding cache duration, in seconds, for unread user notifications when caching is enabled. By default, the cache is refreshed and extended for up to 1800 seconds (30 minutes) after each user activity. To disable sliding expiration, you can set this value to 0.

Secure media files

Introduces a new "Secure Media" feature for additional control over who can access media files. For more info read the Secure Media docs.

Users Module

Enhanced functionality has been implemented, giving developers the ability to control the expiration time of different tokens, such as those for password reset, email confirmation, and email change, which are sent through the email service. Below, you'll find a comprehensive list of configurable options along with their default values:

Class Name Default Expiration Value
ChangeEmailTokenProviderOptions The token is valid by default for 15 minutes.
EmailConfirmationTokenProviderOptions The token is valid by default for 48 hours.
PasswordResetTokenProviderOptions The token is valid by default for 15 minutes.

You may change the default values of these options by using the services.Configure<> method. For instance, to change the EmailConfirmationTokenProviderOptions you can add the following code to your project

services.Configure<EmailConfirmationTokenProviderOptions>(options => options.TokenLifespan = TimeSpan.FromDays(7));

Reloading Tenants

The recent addition in the Pull Request introduces the IShellReleaseManager, enabling you to initiate a shell release at the request's conclusion via the RequestRelease() method. This action is accessible from any service within the application. However, there's no assurance of immediate fulfillment since another service may utilize the same service and suspend the release request later. Should you need to suspend the request to release the shell, you can utilize the SuspendReleaseRequest() method to suspend the tenant release request and the tenant will not be released.

Reloading Tenants Display Drivers

When implementing a site settings display driver, you have the option to reload the shell (restart the tenant). If you choose to do so, manually releasing the shell context using await _shellHost.ReleaseShellContextAsync(_shellSettings) is unnecessary. Instead, you should use the new IShellReleaseManager.RequestRelease(). This ensures that the shell stays intact until all settings are validated. For instance, in ReverseProxySettingsDisplayDriver, the code was modified from this:

public class ReverseProxySettingsDisplayDriver : SiteDisplayDriver<ReverseProxySettings>
{
    //
    // For  example simplicity, other methods are not visible.
    //

    public override async Task<IDisplayResult> UpdateAsync(ISite site, ReverseProxySettings settings, UpdateEditorContext context)
    {
        var user = _httpContextAccessor.HttpContext?.User;

        if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageReverseProxySettings))
        {
            return null;
        }

        //
        // For  example simplicity, other logic are not visible.
        //

        // If the settings are valid, release the current tenant.
        if (context.Updater.ModelState.IsValid)
        {
            await _shellHost.ReleaseShellContextAsync(_shellSettings);
        }

        return await EditAsync(site, settings, context);
    }
}

To this:

public class ReverseProxySettingsDisplayDriver : SiteDisplayDriver<ReverseProxySettings>
{
    private readonly IShellReleaseManager _shellReleaseManager;
    private readonly IHttpContextAccessor _httpContextAccessor;
    private readonly IAuthorizationService _authorizationService;

    public ReverseProxySettingsDisplayDriver(
        // (1) Inject the new service.
        IShellReleaseManager shellReleaseManager
        IHttpContextAccessor httpContextAccessor,
        IAuthorizationService authorizationService)
    {
        _shellReleaseManager = shellReleaseManager;
        _httpContextAccessor = httpContextAccessor;
        _authorizationService = authorizationService;
    }

    //
    // For  example simplicity, other methods are not visible.
    //

    public override async Task<IDisplayResult> UpdateAsync(ISite site, ReverseProxySettings settings, UpdateEditorContext context)
    {
        var user = _httpContextAccessor.HttpContext?.User;

        if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageReverseProxySettings))
        {
            return null;
        }

        //
        // For  example simplicity, other logic are not visible.
        //

        // (2) Request a release at the end of the request.
        _shellReleaseManager.RequestRelease();

        return await EditAsync(site, settings, context);
    }
}

Infrastructure

Prior to this release, if you had a module with multiple features, you had to use the [Feature("your feature id")] attribute to assign IShapeTableProvider, IDataMigration, and IPermissionProvider to a specific feature of the module that had a feature ID other than the module ID. With pull-request 15793, this is no longer needed. However, [Feature("...")] is still required for the Startup class and controllers when you want the controller action to be available only when a specific feature is enabled.

Site Settings

  • New extension methods named GetSettingsAsync<T>() and GetSettingsAsync<T>("") were added to the ISiteService interface. These methods allow you to retrieve specific settings with a single line of code. For example, to get the LoginSettings, you can now use:
await _siteService.GetSettingsAsync<LoginSettings>();

Previously, achieving the same result required more code:

(await _siteService.GetSiteSettingsAsync()).As<LoginSettings>();
  • A new extension method named GetCustomSettingsAsync() was added to the ISiteService interface. This method allows you to retrieve custom settings. For example, to get custom settings of type 'BlogSettings', you can now use:
ContentItem blogSettings = await _siteService.GetCustomSettingsAsync("BlogSettings");

Previously, achieving the same result required more code:

var siteSettings = await _siteService.GetSiteSettingsAsync();
var blogSettings = siteSettings.As<ContentItem>("BlogSettings");

Content Fields

Before this release, the MarkdownField used a single-line input field by default (named the Standard editor) and offered two different editors: Multi-line with a simple textarea and WYSIWYG with a rich editor. Now, by default, it uses a textarea as the Standard editor, and the separate Multi-line option has been removed.

You have nothing to do, fields configured with the Standard or Multi-line editors previously will both continue to work. Note though, that their editors will now be a textarea.

Liquid

The Culture context variable got the new properties for a fuller Liquid culture support: DisplayName, NativeName, and TwoLetterISOLanguageName. These are the same as their .NET counterparts.

A new filter named supported_cultures was added to allow you to get a list of supported cultures. Here is an example of how to print the names of supported cultures using a list:

{% assign cultures = Culture | supported_cultures %}

<ul>
{% for culture in cultures %}
    <li>{{ culture.Name }}</li>
{% endfor %}
</ul>

Adding properties with additional tag helpers

The new <add-property> tag helper can be placed inside the <shape> tag helpers to add properties to the shape. This is similar to prop-* attributes, but you can also include Razor code as the IHtmlContent property value, which was impossible before. See more details here.

Sealing Types

Many type commonly used by modules can be sealed, which improves runtime performance. While it's not mandatory, we recommend that you consider applying this improvement to your own extensions as well. We've implemented this enhancement in the following pull-requests:

!!! note Do not seal classes that are used to create shapes like view-models. Sealing these classes can break your code at runtime, as these classes need to be unsealed to allow for proxy creation.

Workflow Trimming

The Workflows module now has a Trimming feature to automatically clean up old workflow instances. See the corresponding documentation for details.

Obsoleting TrimEnd

The methods public static string TrimEnd(this string value, string trim = "") from OrchardCore.Mvc.Utilities and OrchardCore.ContentManagement.Utilities are being obsoleted and replaced by OrchardCore.ContentManagement.Utilities.TrimEndString(this string value, string suffix). This was done to prepare the code base for the next .NET 9.0 release which has a conflicting method with a different behavior.

Miscellaneous

A new extension has been introduced to simplify the registration of permission providers. Instead of using service.AddScoped<IPermissionProvider, CustomPermissionProvider>(), you can now register it with the more streamlined service.AddPermissionProvider<CustomPermissionProvider>().