You must have an ABP Team or a higher license to be able to create a mobile application.
+
+Mobile application development tutorials are designed for developers who have completed [the web development part of the tutorial](../book-store/index.md) and wish to continue building the mobile version of the application.
+
+## Tutorials
+
+You can choose between two mobile applications: [**.NET MAUI**](../../framework/ui/maui/index.md) or [**React Native**](../../framework/ui/react-native/index.md). Choose your framework and continue building your mobile application!
+
+- Both guides assume you have completed the web development section of the tutorial and have the necessary backend APIs in place.
+- Each guide is self-contained and provides step-by-step instructions to build the mobile app.
+
+### .NET MAUI
+
+The .NET MAUI tutorial walks you through creating a mobile version of your bookstore app using .NET technologies.
+
+* [.NET MAUI - Mobile Application Tutorial](./maui/index.md)
+* [Source Code](https://abp.io/Account/Login?returnUrl=/api/download/samples/bookstore-maui-efcore-mobile)
+
+### React Native
+
+The React Native tutorial provides instructions for building the bookstore app using JavaScript.
+
+* [React Native - Mobile Application Tutorial](./react-native/index.md)
+* [Source Code](https://abp.io/Account/Login?returnUrl=/api/download/samples/bookstore-react-native-mongodb)
\ No newline at end of file
diff --git a/docs/en/tutorials/mobile/maui/index.md b/docs/en/tutorials/mobile/maui/index.md
index a9f1f92d75..c9bb520e57 100644
--- a/docs/en/tutorials/mobile/maui/index.md
+++ b/docs/en/tutorials/mobile/maui/index.md
@@ -2,6 +2,8 @@
## About This Tutorial
+> You must have an ABP Team or a higher license to be able to create a mobile application.
+
This tutorial assumes that you have completed the [Web Application Development tutorial](../../book-store/part-01.md) and built an ABP based application named `Acme.BookStore` with [MAUI](../../../get-started/maui.md) as the mobile option. Therefore, if you haven't completed the [Web Application Development tutorial](../../book-store/part-01.md), you either need to complete it or download the source code from down below and follow this tutorial.
In this tutorial, we will only focus on the UI side of the `Acme.BookStore` application and we will implement the CRUD operations for a MAUI mobile application. This tutorial follows the [MVVM (Model-View-ViewModel) Pattern ](https://learn.microsoft.com/en-us/dotnet/architecture/maui/mvvm), which separates the UI from the business logic of an application.
diff --git a/docs/en/tutorials/mobile/react-native/index.md b/docs/en/tutorials/mobile/react-native/index.md
index 74abfa3418..2dfa3df957 100644
--- a/docs/en/tutorials/mobile/react-native/index.md
+++ b/docs/en/tutorials/mobile/react-native/index.md
@@ -4,6 +4,8 @@ React Native mobile option is *available for* ***Team*** *or higher licenses*. T
## About This Tutorial
+> You must have an ABP Team or a higher license to be able to create a mobile application.
+
- This tutorial assumes that you have completed the [Web Application Development tutorial](../../book-store/part-01.md) and built an ABP based application named `Acme.BookStore` with [React Native](../../../framework/ui/react-native) as the mobile option.. Therefore, if you haven't completed the [Web Application Development tutorial](../../book-store/part-01.md), you either need to complete it or download the source code from down below and follow this tutorial.
- In this tutorial, we will only focus on the UI side of the `Acme.BookStore` application and will implement the CRUD operations.
- Before starting, please make sure that the [React Native Development Environment](../../../framework/ui/react-native/index.md) is ready on your machine.
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Libs/AbpMvcLibsErrorPage.Designer.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Libs/AbpMvcLibsErrorPage.Designer.cs
index 9d30ea018c..4fe8e51000 100644
--- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Libs/AbpMvcLibsErrorPage.Designer.cs
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Libs/AbpMvcLibsErrorPage.Designer.cs
@@ -27,20 +27,100 @@ using Volo.Abp.AspNetCore.RazorViews;
#line hidden
#nullable disable
WriteLiteral(@"
-
-
-
- Error - The Libs folder is missing!
-
-
- ⚠️ The Libs folder under the wwwroot/libs directory is empty!
-
+
+
+
+
+
+ Error - The Libs Folder is Missing!
+
+
+
+
+ ⚠️ The Libs folder is missing!
+
+
The Libs folder contains mandatory NPM Packages for running the project.
-
- Make sure you run the abp install-libs CLI tool command.
-
- For more information, check out the ABP CLI documentation
-
+ Make sure you run the abp install-lib");
+ WriteLiteral(@"s CLI tool command.
+
+ If your application does not use any client-side libraries, you can disable this check by setting
+ AbpMvcLibsOptions.CheckLibs to false, as shown below:
+
+
+Configure<AbpMvcLibsOptions>(options =>
+{
+ options.CheckLibs = false;
+});
+ For more information, check out the ABP CLI documentation.
+
+
+
");
}
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Libs/AbpMvcLibsErrorPage.cshtml b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Libs/AbpMvcLibsErrorPage.cshtml
index 5a5d6c8d19..7b15ce94bf 100644
--- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Libs/AbpMvcLibsErrorPage.cshtml
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Libs/AbpMvcLibsErrorPage.cshtml
@@ -5,18 +5,96 @@
Response.StatusCode = 500;
}
-
-
-
- Error - The Libs folder is missing!
-
-
- ⚠️ The Libs folder under the wwwroot/libs directory is empty!
-
+
+
+
+
+
+ Error - The Libs Folder is Missing!
+
+
+
+
+ ⚠️ The Libs folder is missing!
+
+
The Libs folder contains mandatory NPM Packages for running the project.
-
- Make sure you run the abp install-libs CLI tool command.
-
- For more information, check out the ABP CLI documentation
-
+ Make sure you run the abp install-libs CLI tool command.
+
+ If your application does not use any client-side libraries, you can disable this check by setting
+ AbpMvcLibsOptions.CheckLibs to false, as shown below:
+
+
+Configure<AbpMvcLibsOptions>(options =>
+{
+ options.CheckLibs = false;
+});
+ For more information, check out the ABP CLI documentation.
+
+
+
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Libs/AbpMvcLibsService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Libs/AbpMvcLibsService.cs
index 410260674b..dc6faf723c 100644
--- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Libs/AbpMvcLibsService.cs
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Libs/AbpMvcLibsService.cs
@@ -64,7 +64,7 @@ public class AbpMvcLibsService : IAbpMvcLibsService, ITransientDependency
var webHostEnvironment = httpContext.RequestServices.GetRequiredService();
if (webHostEnvironment.WebRootPath.IsNullOrWhiteSpace())
{
- logger.LogWarning("The 'WebRootPath' is not set! The 'CheckLibs' feature is disabled!");
+ logger.LogInformation("The 'WebRootPath' is not set, The 'CheckLibs' feature not needed.");
return Task.FromResult(true);
}
@@ -72,7 +72,8 @@ public class AbpMvcLibsService : IAbpMvcLibsService, ITransientDependency
var libsFolder = fileProvider.GetDirectoryContents("/libs");
if (!libsFolder.Exists || !libsFolder.Any())
{
- logger.LogError("The 'wwwroot/libs' folder does not exist or empty!");
+ logger.LogError("The 'wwwroot/libs' folder does not exist or empty! " +
+ "If your application does not use any client-side libraries, you can disable this check by setting 'AbpMvcLibsOptions.CheckLibs' to 'false'");
return Task.FromResult(false);
}
}
diff --git a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/AbpSecurityHeadersMiddleware.cs b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/AbpSecurityHeadersMiddleware.cs
index 4542dd32e1..2d231c46ee 100644
--- a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/AbpSecurityHeadersMiddleware.cs
+++ b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/AbpSecurityHeadersMiddleware.cs
@@ -23,6 +23,14 @@ public class AbpSecurityHeadersMiddleware : AbpMiddlewareBase, ITransientDepende
public async override Task InvokeAsync(HttpContext context, RequestDelegate next)
{
+ var endpoint = context.GetEndpoint();
+
+ if (endpoint?.Metadata.GetMetadata() != null)
+ {
+ await next.Invoke(context);
+ return;
+ }
+
/*X-Content-Type-Options header tells the browser to not try and โguessโ what a mimetype of a resource might be, and to just take what mimetype the server has returned as fact.*/
AddHeader(context, "X-Content-Type-Options", "nosniff");
@@ -35,14 +43,6 @@ public class AbpSecurityHeadersMiddleware : AbpMiddlewareBase, ITransientDepende
var requestAcceptTypeHtml = context.Request.Headers["Accept"].Any(x =>
x!.Contains("text/html") || x.Contains("*/*") || x.Contains("application/xhtml+xml"));
- var endpoint = context.GetEndpoint();
-
- if (endpoint?.Metadata.GetMetadata() != null)
- {
- await next.Invoke(context);
- return;
- }
-
if (!requestAcceptTypeHtml
|| !Options.Value.UseContentSecurityPolicyHeader
|| await AlwaysIgnoreContentTypes(context)
diff --git a/framework/src/Volo.Abp.Authorization/Microsoft/AspNetCore/Authorization/AbpAuthorizationServiceExtensions.cs b/framework/src/Volo.Abp.Authorization/Microsoft/AspNetCore/Authorization/AbpAuthorizationServiceExtensions.cs
index c66d74b381..3dfc5218d0 100644
--- a/framework/src/Volo.Abp.Authorization/Microsoft/AspNetCore/Authorization/AbpAuthorizationServiceExtensions.cs
+++ b/framework/src/Volo.Abp.Authorization/Microsoft/AspNetCore/Authorization/AbpAuthorizationServiceExtensions.cs
@@ -108,6 +108,11 @@ public static class AbpAuthorizationServiceExtensions
return (await authorizationService.AuthorizeAsync(resource, policyName)).Succeeded;
}
+ ///
+ /// Checks if CurrentPrincipal meets a specific authorization policy, throwing an if not.
+ ///
+ /// The providing authorization.
+ /// The name of the policy to evaluate.
public static async Task CheckAsync(this IAuthorizationService authorizationService, string policyName)
{
if (!await authorizationService.IsGrantedAsync(policyName))
@@ -117,6 +122,12 @@ public static class AbpAuthorizationServiceExtensions
}
}
+ ///
+ /// Checks if CurrentPrincipal meets a specific requirement for the specified resource, throwing an if not.
+ ///
+ /// The providing authorization.
+ /// The resource to evaluate the policy against.
+ /// The requirement to evaluate the policy against.
public static async Task CheckAsync(this IAuthorizationService authorizationService, object resource, IAuthorizationRequirement requirement)
{
if (!await authorizationService.IsGrantedAsync(resource, requirement))
@@ -126,6 +137,12 @@ public static class AbpAuthorizationServiceExtensions
}
}
+ ///
+ /// Checks if CurrentPrincipal meets a specific authorization policy against the specified resource, throwing an if not.
+ ///
+ /// The providing authorization.
+ /// The resource to evaluate the policy against.
+ /// The policy to evaluate.
public static async Task CheckAsync(this IAuthorizationService authorizationService, object resource, AuthorizationPolicy policy)
{
if (!await authorizationService.IsGrantedAsync(resource, policy))
@@ -135,6 +152,11 @@ public static class AbpAuthorizationServiceExtensions
}
}
+ ///
+ /// Checks if CurrentPrincipal meets a specific authorization policy, throwing an if not.
+ ///
+ /// The providing authorization.
+ /// The policy to evaluate.
public static async Task CheckAsync(this IAuthorizationService authorizationService, AuthorizationPolicy policy)
{
if (!await authorizationService.IsGrantedAsync(policy))
@@ -143,6 +165,12 @@ public static class AbpAuthorizationServiceExtensions
}
}
+ ///
+ /// Checks if CurrentPrincipal meets a specific authorization policy against the specified resource, throwing an if not.
+ ///
+ /// The providing authorization.
+ /// The resource to evaluate the policy against.
+ /// The requirements to evaluate the policy against.
public static async Task CheckAsync(this IAuthorizationService authorizationService, object resource, IEnumerable requirements)
{
if (!await authorizationService.IsGrantedAsync(resource, requirements))
@@ -152,6 +180,12 @@ public static class AbpAuthorizationServiceExtensions
}
}
+ ///
+ /// Checks if CurrentPrincipal meets a specific authorization policy against the specified resource, throwing an if not.
+ ///
+ /// The providing authorization.
+ /// The resource to evaluate the policy against.
+ /// The name of the policy to evaluate.
public static async Task CheckAsync(this IAuthorizationService authorizationService, object resource, string policyName)
{
if (!await authorizationService.IsGrantedAsync(resource, policyName))
diff --git a/framework/src/Volo.Abp.BlazoriseUI/Components/AbpExtensibleDataGrid.razor b/framework/src/Volo.Abp.BlazoriseUI/Components/AbpExtensibleDataGrid.razor
index 29010dded6..2547a7930c 100644
--- a/framework/src/Volo.Abp.BlazoriseUI/Components/AbpExtensibleDataGrid.razor
+++ b/framework/src/Volo.Abp.BlazoriseUI/Components/AbpExtensibleDataGrid.razor
@@ -112,7 +112,7 @@
Sortable="@column.Sortable"
Displayable="column.Visible">
- @(GetConvertedFieldValue(context, column))
+ @((MarkupString)GetConvertedFieldValue(context, column))
}
@@ -140,7 +140,7 @@
{
if (column.ValueConverter != null)
{
- @(GetConvertedFieldValue(context, column))
+ @((MarkupString)GetConvertedFieldValue(context, column))
}
else
{
diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/EmailSenderBase.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/EmailSenderBase.cs
index dacbb195ab..4f07c8a1c5 100644
--- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/EmailSenderBase.cs
+++ b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/EmailSenderBase.cs
@@ -92,7 +92,7 @@ public abstract class EmailSenderBase : IEmailSender
public virtual async Task QueueAsync(string to, string subject, string body, bool isBodyHtml = true, AdditionalEmailSendingArgs? additionalEmailSendingArgs = null)
{
- ValidateEmailAddress(to);
+ await ValidateEmailAddressAsync(to);
if (!BackgroundJobManager.IsAvailable())
{
@@ -115,7 +115,7 @@ public abstract class EmailSenderBase : IEmailSender
public virtual async Task QueueAsync(string from, string to, string subject, string body, bool isBodyHtml = true, AdditionalEmailSendingArgs? additionalEmailSendingArgs = null)
{
- ValidateEmailAddress(to);
+ await ValidateEmailAddressAsync(to);
if (!BackgroundJobManager.IsAvailable())
{
@@ -176,13 +176,16 @@ public abstract class EmailSenderBase : IEmailSender
}
}
- private static void ValidateEmailAddress(string emailAddress)
+ protected virtual Task ValidateEmailAddressAsync(string emailAddress)
{
- if(ValidationHelper.IsValidEmailAddress(emailAddress))
+ try
{
- return;
+ _ = new MailAddressCollection { emailAddress };
+ return Task.CompletedTask;
+ }
+ catch (Exception e)
+ {
+ throw new ArgumentException($"Email address '{emailAddress}' is not valid!");
}
-
- throw new ArgumentException($"Email address '{emailAddress}' is not valid!");
}
}
diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo.Abp.EntityFrameworkCore.Oracle.Devart.csproj b/framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo.Abp.EntityFrameworkCore.Oracle.Devart.csproj
index db32e24bef..bfe9bea75a 100644
--- a/framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo.Abp.EntityFrameworkCore.Oracle.Devart.csproj
+++ b/framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo.Abp.EntityFrameworkCore.Oracle.Devart.csproj
@@ -22,7 +22,6 @@