Merge branch 'dev' into net7

pull/13626/head
maliming 2 years ago
commit f619e674c9

@ -505,6 +505,7 @@ abp login <username> # Allows you to enter your
abp login <username> -p <password> # Specify the password as a parameter (password is visible)
abp login <username> --organization <organization> # If you have multiple organizations, you need set your active organization
abp login <username> -p <password> -o <organization> # You can enter both your password and organization in the same command
abp login <username> --device # Use device login flow
```
> When using the -p parameter, be careful as your password will be visible. It's useful for CI/CD automation pipelines.

@ -84,6 +84,16 @@ ABP provides a distributed locking abstraction with an implementation made with
However, the distributed lock system works in-process by default. That means it is not distributed actually, unless you configure a distributed lock provider. So, please follow the [distributed lock](../Distributed-Locking.md) document to configure a provider for your application, if it is not already configured.
## Configuring SignalR
ABP provides [SignalR](../SignalR-Integration.md) integration packages to simplify integration and usage. SignalR can be used whenever you need to add real-time web functionality (real-time messaging, real-time notification etc.) into your application.
SignalR requires that all HTTP requests for a specific connection be handled (needs to keep track of all its connections) by the same server process. So, when SignalR is running on a clustered environment (with multiple servers) **"sticky sessions"** must be used.
If you are considering [scaling out](https://learn.microsoft.com/en-us/aspnet/core/signalr/scale?view=aspnetcore-6.0#scale-out) your servers and don't want to have inconsistency with the active socket connections, you can use [Azure SignalR Service](https://learn.microsoft.com/en-us/aspnet/core/signalr/scale?view=aspnetcore-6.0#azure-signalr-service) or [Redis backplane](https://learn.microsoft.com/en-us/aspnet/core/signalr/scale?view=aspnetcore-6.0#redis-backplane).
> To learn more about how to host and scale SignalR in a clustered environment, please check the [ASP.NET Core SignalR hosting and scaling](https://learn.microsoft.com/en-us/aspnet/core/signalr/scale?view=aspnetcore-6.0).
## Implementing Background Workers
ASP.NET Core provides [hosted services](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services) and ABP provides [background workers](../Background-Workers.md) to perform tasks in background threads in your application.
@ -96,4 +106,4 @@ We suggest you to use one of the following approaches to overcome the problem:
* Implement your background workers so that they work in a clustered environment without any problem. Using the [distributed lock](../Distributed-Locking.md) to ensure concurrency control is a way of doing that. A background worker in an application instance may handle a distributed lock, so the workers in other application instances will wait for the lock. In this way, only one worker does the actual work, while others wait in idle. If you implement this, your workers run safely without caring about how the application is deployed.
* Stop the background workers (set `AbpBackgroundWorkerOptions.IsEnabled` to `false`) in all application instances except one of them, so only the single instance runs the workers.
* Stop the background workers (set `AbpBackgroundWorkerOptions.IsEnabled` to `false`) in all application instances and create a dedicated application (maybe a console application running in its own container or a Windows Service running in the background) to execute all the background tasks. This can be a good option if your background workers consume high system resources (CPU, RAM or Disk), so you can deploy that background application to a dedicated server and your background tasks don't affect your application's performance.
* Stop the background workers (set `AbpBackgroundWorkerOptions.IsEnabled` to `false`) in all application instances and create a dedicated application (maybe a console application running in its own container or a Windows Service running in the background) to execute all the background tasks. This can be a good option if your background workers consume high system resources (CPU, RAM or Disk), so you can deploy that background application to a dedicated server and your background tasks don't affect your application's performance.

@ -15,7 +15,7 @@ ABP platform provide basic [React Native](https://reactnative.dev/) startup temp
Please follow the steps below to prepare your development environment for React Native.
1. **Install Node.js:** Please visit [Node.js downloads page](https://nodejs.org/en/download/) and download proper Node.js v12 or v14 installer for your OS. An alternative is to install [NVM](https://github.com/nvm-sh/nvm) and use it to have multiple versions of Node.js in your operating system.
1. **Install Node.js:** Please visit [Node.js downloads page](https://nodejs.org/en/download/) and download proper Node.js v16 or v18 installer for your OS. An alternative is to install [NVM](https://github.com/nvm-sh/nvm) and use it to have multiple versions of Node.js in your operating system.
2. **[Optional] Install Yarn:** You may install Yarn v1 (not v2) following the instructions on [the installation page](https://classic.yarnpkg.com/en/docs/install). Yarn v1 delivers an arguably better developer experience compared to npm v6 and below. You may skip this step and work with npm, which is built-in in Node.js, instead.
3. **[Optional] Install VS Code:** [VS Code](https://code.visualstudio.com/) is a free, open-source IDE which works seamlessly with TypeScript. Although you can use any IDE including Visual Studio or Rider, VS Code will most likely deliver the best developer experience when it comes to React Native projects.
4. **Install an Emulator:** React Native applications need an Android emulator or an iOS simulator to run on your OS. See the [Android Studio Emulator](https://docs.expo.io/workflow/android-simulator/) or [iOS Simulator](https://docs.expo.io/workflow/ios-simulator/) on expo.io documentation to learn how to set up an emulator.

@ -21,7 +21,7 @@ The following tools should be installed on your development machine:
* An IDE (e.g. [Visual Studio](https://visualstudio.microsoft.com/vs/)) that supports [.NET 6.0+](https://dotnet.microsoft.com/download/dotnet) development.
{{ if UI != "Blazor" }}
* [Node v12 or v14](https://nodejs.org/)
* [Node v16 or v18](https://nodejs.org/)
* [Yarn v1.20+ (not v2)](https://classic.yarnpkg.com/en/docs/install) <sup id="a-yarn">[1](#f-yarn)</sup> or npm v6+ (already installed with Node)
{{ end }}
{{ if Tiered == "Yes" }}

@ -27,7 +27,7 @@
with
```csharp
typeof(AbpAccountWebModule),
typeof(AbpAccountWebOpenIddictModule),
```
- In **MyApplicationWebModule.cs** `ConfigureServices` method **update authentication configuration**:

@ -293,6 +293,8 @@ PreConfigure<AbpOpenIddictAspNetCoreOptions>(options =>
- `UpdateAbpClaimTypes(default: true)`: Updates `AbpClaimTypes` to be compatible with the Openiddict claims.
- `AddDevelopmentEncryptionAndSigningCertificate(default: true)`: Registers (and generates if necessary) a user-specific development encryption/development signing certificate.
> `AddDevelopmentEncryptionAndSigningCertificate` cannot be used in applications deployed on IIS or Azure App Service: trying to use them on IIS or Azure App Service will result in an exception being thrown at runtime (unless the application pool is configured to load a user profile). To avoid that, consider creating self-signed certificates and storing them in the X.509 certificates store of the host machine(s). Please refer to: https://documentation.openiddict.com/configuration/encryption-and-signing-credentials.html#registering-a-development-certificate
#### Automatically Removing Orphaned Tokens/Authorizations
The background task that automatically removes orphaned tokens/authorizations. This can be configured by `TokenCleanupOptions` to manage it.

@ -11,6 +11,14 @@ More than one preview releases (like 3.1.0-rc.2 and 3.1.0-rc.3) might be publish
## Using the Preview Versions
### Update the CLI
Before creating or updating an existing solution make sure to update the CLI to the latest preview version, for example:
````bash
dotnet tool update --global Volo.Abp.Cli --version 6.0.0-rc.2
````
### New Solutions
To create a project for testing the preview version, you can select the "**preview**" option on the [download page](https://abp.io/get-started) or use the "**--preview**" parameter with the [ABP CLI](CLI.md) new command:

@ -241,13 +241,7 @@ $(function () {
{
title: l('PublishDate'),
data: "publishDate",
render: function (data) {
return luxon
.DateTime
.fromISO(data, {
locale: abp.localization.currentCulture.name
}).toLocaleString();
}
dataFormat: "datetime"
},
{
title: l('Price'),
@ -255,13 +249,7 @@ $(function () {
},
{
title: l('CreationTime'), data: "creationTime",
render: function (data) {
return luxon
.DateTime
.fromISO(data, {
locale: abp.localization.currentCulture.name
}).toLocaleString(luxon.DateTime.DATETIME_SHORT);
}
dataFormat: "datetime"
}
]
})
@ -439,13 +427,7 @@ $(function () {
{
title: l('PublishDate'),
data: "publishDate",
render: function (data) {
return luxon
.DateTime
.fromISO(data, {
locale: abp.localization.currentCulture.name
}).toLocaleString();
}
dataFormat: "datetime"
},
{
title: l('Price'),
@ -453,13 +435,7 @@ $(function () {
},
{
title: l('CreationTime'), data: "creationTime",
render: function (data) {
return luxon
.DateTime
.fromISO(data, {
locale: abp.localization.currentCulture.name
}).toLocaleString(luxon.DateTime.DATETIME_SHORT);
}
dataFormat: "datetime"
}
]
})
@ -587,13 +563,7 @@ $(function () {
{
title: l('PublishDate'),
data: "publishDate",
render: function (data) {
return luxon
.DateTime
.fromISO(data, {
locale: abp.localization.currentCulture.name
}).toLocaleString();
}
dataFormat: "datetime"
},
{
title: l('Price'),
@ -601,13 +571,7 @@ $(function () {
},
{
title: l('CreationTime'), data: "creationTime",
render: function (data) {
return luxon
.DateTime
.fromISO(data, {
locale: abp.localization.currentCulture.name
}).toLocaleString(luxon.DateTime.DATETIME_SHORT);
}
dataFormat: "datetime"
}
]
})

@ -17,7 +17,7 @@ You can find the source code of the completed application [here](https://github.
## Pre-Requirements
* An IDE (e.g. [Visual Studio](https://visualstudio.microsoft.com/vs/)) that supports [.NET 6.0+](https://dotnet.microsoft.com/download/dotnet) development.
* [Node v14.x](https://nodejs.org/)
* [Node v16.x](https://nodejs.org/)
{{if DB=="Mongo"}}

@ -17,7 +17,7 @@ You can find the source code of the completed application [here](https://github.
## Pre-Requirements
* An IDE (e.g. [Visual Studio](https://visualstudio.microsoft.com/vs/)) that supports [.NET 6.0+](https://dotnet.microsoft.com/download/dotnet) development.
* [Node v14.x](https://nodejs.org/)
* [Node v16.x](https://nodejs.org/)
{{if DB=="Mongo"}}

@ -4,7 +4,7 @@
Please follow the steps below to prepare your development environment for Angular.
1. **Install Node.js:** Please visit [Node.js downloads page](https://nodejs.org/en/download/) and download proper Node.js v12 or v14 installer for your OS. An alternative is to install [NVM](https://github.com/nvm-sh/nvm) and use it to have multiple versions of Node.js in your operating system.
1. **Install Node.js:** Please visit [Node.js downloads page](https://nodejs.org/en/download/) and download proper Node.js v16 or v18 installer for your OS. An alternative is to install [NVM](https://github.com/nvm-sh/nvm) and use it to have multiple versions of Node.js in your operating system.
2. **[Optional] Install Yarn:** You may install Yarn v1 (not v2) following the instructions on [the installation page](https://classic.yarnpkg.com/en/docs/install). Yarn v1 delivers an arguably better developer experience compared to npm v6 and below. You may skip this step and work with npm, which is built-in in Node.js, instead.
3. **[Optional] Install VS Code:** [VS Code](https://code.visualstudio.com/) is a free, open-source IDE which works seamlessly with TypeScript. Although you can use any IDE including Visual Studio or Rider, VS Code will most likely deliver the best developer experience when it comes to Angular projects. ABP project templates even contain plugin recommendations for VS Code users, which VS Code will ask you to install when you open the Angular project folder. Here is a list of recommended extensions:
- [Angular Language Service](https://marketplace.visualstudio.com/items?itemName=angular.ng-template)

@ -20,5 +20,4 @@ if (abp.auth.isGranted('DeleteUsers')) {
* ` abp.auth.isAnyGranted(...)`: Gets one or more permission/policy names and returns `true` if at least one of them has granted.
* `abp.auth.areAllGranted(...)`: Gets one or more permission/policy names and returns `true` if all of them of them have granted.
* `abp.auth.policies`: This is an object where its keys are the permission/policy names. You can find all permission/policy names here.
* `abp.auth.grantedPolicies`: This is an object where its keys are the permission/policy names. You can find the granted permission/policy names here.

@ -409,6 +409,7 @@ abp login <username> # Allows you to enter your
abp login <username> -p <password> # Specify the password as a parameter (password is visible)
abp login <username> --organization <organization> # If you have multiple organizations, you need set your active organization
abp login <username> -p <password> -o <organization> # You can enter both your password and organization in the same command
abp login <username> --device # Use device login flow
```
> 当使用-p参数,请注意,因为你的密码是可见的. 它对于CI / CD自动化管道很有用.

@ -21,7 +21,7 @@
* 一个集成开发环境 (比如: [Visual Studio](https://visualstudio.microsoft.com/vs/)) 它需要支持 [.NET 6.0+](https://dotnet.microsoft.com/download/dotnet) 的开发.
{{ if UI != "Blazor" }}
* [Node v12 或 v14](https://nodejs.org/)
* [Node v16 或 v18](https://nodejs.org/)
* [Yarn v1.20+ (不是v2)](https://classic.yarnpkg.com/en/docs/install) <sup id="a-yarn">[1](#f-yarn)</sup> 或 npm v6+ (已跟随Node一起安装)
{{ end }}
{{ if Tiered == "Yes" }}

@ -26,7 +26,7 @@
{{if UI=="NG"}}
* [Node v14.x](https://nodejs.org/)
* [Node v16.x](https://nodejs.org/)
{{end}}

@ -6,13 +6,10 @@ namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations;
[Serializable]
public class ApplicationAuthConfigurationDto
{
public Dictionary<string, bool> Policies { get; set; }
public Dictionary<string, bool> GrantedPolicies { get; set; }
public ApplicationAuthConfigurationDto()
{
Policies = new Dictionary<string, bool>();
GrantedPolicies = new Dictionary<string, bool>();
}
}

@ -185,8 +185,6 @@ public class AbpApplicationConfigurationAppService : ApplicationService, IAbpApp
foreach (var policyName in otherPolicyNames)
{
authConfig.Policies[policyName] = true;
if (await _authorizationService.IsGrantedAsync(policyName))
{
authConfig.GrantedPolicies[policyName] = true;
@ -196,7 +194,6 @@ public class AbpApplicationConfigurationAppService : ApplicationService, IAbpApp
var result = await _permissionChecker.IsGrantedAsync(abpPolicyNames.ToArray());
foreach (var (key, value) in result.Result)
{
authConfig.Policies[key] = true;
if (value == PermissionGrantResult.Granted)
{
authConfig.GrantedPolicies[key] = true;
@ -228,10 +225,10 @@ public class AbpApplicationConfigurationAppService : ApplicationService, IAbpApp
foreach (var resourceName in resourceNames)
{
var dictionary = new Dictionary<string, string>();
var localizer = await StringLocalizerFactory
.CreateByResourceNameOrNullAsync(resourceName);
if (localizer != null)
{
foreach (var localizedString in await localizer.GetAllStringsAsync())

@ -1,4 +1,6 @@
using System.Threading.Tasks;
using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection;
@ -25,13 +27,31 @@ public class DataSeeder : IDataSeeder, ITransientDependency
{
using (var scope = ServiceScopeFactory.CreateScope())
{
foreach (var contributorType in Options.Contributors)
if (context.Properties.ContainsKey(DataSeederExtensions.SeedInSeparateUow))
{
var contributor = (IDataSeedContributor)scope
.ServiceProvider
.GetRequiredService(contributorType);
var manager = scope.ServiceProvider.GetRequiredService<IUnitOfWorkManager>();
foreach (var contributorType in Options.Contributors)
{
var options = context.Properties.TryGetValue(DataSeederExtensions.SeedInSeparateUowOptions, out var uowOptions)
? (AbpUnitOfWorkOptions) uowOptions
: new AbpUnitOfWorkOptions();
var requiresNew = context.Properties.TryGetValue(DataSeederExtensions.SeedInSeparateUowRequiresNew, out var obj) && (bool) obj;
await contributor.SeedAsync(context);
using (var uow = manager.Begin(options, requiresNew))
{
var contributor = (IDataSeedContributor)scope.ServiceProvider.GetRequiredService(contributorType);
await contributor.SeedAsync(context);
await uow.CompleteAsync();
}
}
}
else
{
foreach (var contributorType in Options.Contributors)
{
var contributor = (IDataSeedContributor)scope.ServiceProvider.GetRequiredService(contributorType);
await contributor.SeedAsync(context);
}
}
}
}

@ -1,12 +1,26 @@
using System;
using System.Threading.Tasks;
using Volo.Abp.Uow;
namespace Volo.Abp.Data;
public static class DataSeederExtensions
{
public const string SeedInSeparateUow = "__SeedInSeparateUow";
public const string SeedInSeparateUowOptions = "__SeedInSeparateUowOptions";
public const string SeedInSeparateUowRequiresNew = "__SeedInSeparateUowRequiresNew";
public static Task SeedAsync(this IDataSeeder seeder, Guid? tenantId = null)
{
return seeder.SeedAsync(new DataSeedContext(tenantId));
}
public static Task SeedInSeparateUowAsync(this IDataSeeder seeder, Guid? tenantId = null, AbpUnitOfWorkOptions options = null, bool requiresNew = false)
{
var context = new DataSeedContext(tenantId);
context.WithProperty(SeedInSeparateUow, true);
context.WithProperty(SeedInSeparateUowOptions, options);
context.WithProperty(SeedInSeparateUowRequiresNew, requiresNew);
return seeder.SeedAsync(context);
}
}

@ -174,7 +174,6 @@ public static class ModuleExtensionConfigurationHelper
property.DefaultValue = propertyConfig.DefaultValue;
property.DefaultValueFactory = propertyConfig.DefaultValueFactory;
property.Lookup = propertyConfig.UI.Lookup;
property.Configuration.Clear();
foreach (var configuration in propertyConfig.Configuration)
{
property.Configuration[configuration.Key] = configuration.Value;

@ -0,0 +1,43 @@
using System;
using System.Data;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using NSubstitute;
using Volo.Abp.Modularity;
using Volo.Abp.Testing;
using Volo.Abp.Uow;
using Xunit;
namespace Volo.Abp.Data;
public class DataSeederExtensions_Tests : AbpIntegratedTest<DataSeederExtensions_Tests.TestModule>
{
private IDataSeeder _dataSeeder;
protected override void AfterAddApplication(IServiceCollection services)
{
_dataSeeder = Substitute.For<IDataSeeder>();
services.Replace(ServiceDescriptor.Singleton(_dataSeeder));
base.AfterAddApplication(services);
}
[Fact]
public void SeedInSeparateUowAsync()
{
var tenantId = Guid.NewGuid();
_dataSeeder.SeedInSeparateUowAsync(tenantId, new AbpUnitOfWorkOptions(true, IsolationLevel.Serializable, 888), true);
_dataSeeder.Received().SeedAsync(Arg.Is<DataSeedContext>(x => x.TenantId == tenantId &&
x.Properties[DataSeederExtensions.SeedInSeparateUow].To<bool>() == true &&
x.Properties[DataSeederExtensions.SeedInSeparateUowOptions].As<AbpUnitOfWorkOptions>().IsTransactional == true &&
x.Properties[DataSeederExtensions.SeedInSeparateUowOptions].As<AbpUnitOfWorkOptions>().IsolationLevel == IsolationLevel.Serializable &&
x.Properties[DataSeederExtensions.SeedInSeparateUowOptions].As<AbpUnitOfWorkOptions>().Timeout == 888 &&
x.Properties[DataSeederExtensions.SeedInSeparateUowRequiresNew].To<bool>() == true));
}
[DependsOn(typeof(AbpDataModule))]
public class TestModule : AbpModule
{
}
}

@ -36,6 +36,7 @@
"PersonalSettingsSaved": "تم حفظ الإعدادات الشخصية",
"PasswordChanged": "تم تغيير كلمة المرور",
"NewPasswordConfirmFailed": "يرجى تأكيد كلمة المرور الجديدة.",
"NewPasswordSameAsOld": "يجب أن تكون كلمة المرور الجديدة مختلفة عن كلمة المرور الحالية.",
"Manage": "إدارة",
"MyAccount": "حسابي",
"DisplayName:Abp.Account.IsSelfRegistrationEnabled": "هل تم تمكين التسجيل الذاتي",

@ -35,6 +35,7 @@
"PersonalSettingsSaved": "Osobní nastavení uloženo",
"PasswordChanged": "Heslo změněno",
"NewPasswordConfirmFailed": "Potvrďte nové heslo.",
"NewPasswordSameAsOld": "Nové heslo musí být odlišné od současného.",
"Manage": "Spravovat",
"MyAccount": "Můj účet",
"DisplayName:Abp.Account.IsSelfRegistrationEnabled": "Je povolena automatická registrace",

@ -35,6 +35,7 @@
"PersonalSettingsSaved": "Personlige indstillinger gemt",
"PasswordChanged": "Kodeord gemt",
"NewPasswordConfirmFailed": "Bekræft venligst det nye kodeord.",
"NewPasswordSameAsOld": "Det nye kodeord skal være anderledes end det gamle.",
"Manage": "Administrer",
"MyAccount": "Min konto",
"DisplayName:Abp.Account.IsSelfRegistrationEnabled": "Er registrering aktiveret",

@ -36,6 +36,7 @@
"PersonalSettingsSaved": "Persönliche Einstellungen gespeichert",
"PasswordChanged": "Passwort geändert",
"NewPasswordConfirmFailed": "Bitte bestätigen Sie das neue Passwort.",
"NewPasswordSameAsOld": "Das neue Passwort muss sich vom alten Passwort unterscheiden.",
"Manage": "Verwalten",
"MyAccount": "Mein Benutzerkonto",
"DisplayName:Abp.Account.IsSelfRegistrationEnabled": "Selbstregistrierung ist aktiviert",

@ -35,6 +35,7 @@
"PersonalSettingsSaved": "Persönliche Einstellungen gespeichert",
"PasswordChanged": "Passwort geändert",
"NewPasswordConfirmFailed": "Bitte bestätigen Sie das neue Passwort.",
"NewPasswordSameAsOld": "Das neue Passwort muss sich vom alten Passwort unterscheiden.",
"Manage": "Verwalten",
"MyAccount": "Mein Konto",
"DisplayName:Abp.Account.IsSelfRegistrationEnabled": "Ist die Selbstregistrierung aktiviert",

@ -36,6 +36,7 @@
"PersonalSettingsSaved": "Οι προσωπικές ρυθμίσεις αποθηκεύτηκαν",
"PasswordChanged": "Ο κωδικός πρόσβασης άλλαξε",
"NewPasswordConfirmFailed": "Παρακαλώ επιβεβαιώστε τον νέο κωδικό πρόσβασης.",
"NewPasswordSameAsOld": "Ο νέος κωδικός πρόσβασης πρέπει να είναι διαφορετικός από τον τρέχοντα.",
"Manage": "Διαχείρηση",
"MyAccount": "Ο λογαριασμός μου",
"DisplayName:Abp.Account.IsSelfRegistrationEnabled": "Είναι ενεργοποιημένη η αυτοεγγραφή",

@ -35,6 +35,7 @@
"PersonalSettingsSaved": "Personal settings saved",
"PasswordChanged": "Password changed",
"NewPasswordConfirmFailed": "Please confirm the new password.",
"NewPasswordSameAsOld": "The new password must be different from the old one.",
"Manage": "Manage",
"MyAccount": "My account",
"DisplayName:Abp.Account.IsSelfRegistrationEnabled": "Is self-registration enabled",

@ -36,6 +36,7 @@
"PersonalSettingsSaved": "Personal settings saved",
"PasswordChanged": "Password changed",
"NewPasswordConfirmFailed": "Please confirm the new password.",
"NewPasswordSameAsOld": "New password must be different from the old one.",
"Manage": "Manage",
"MyAccount": "My account",
"DisplayName:Abp.Account.IsSelfRegistrationEnabled": "Is self-registration enabled",

@ -32,6 +32,7 @@
"PersonalSettingsSaved": "Ajustes personales guardados",
"PasswordChanged": "Cambia la contraseña",
"NewPasswordConfirmFailed": "Por favor, confirme la nueva contraseña.",
"NewPasswordSameAsOld": "La nueva contraseña debe ser diferente de la contraseña actual.",
"Manage": "Administrar",
"MyAccount": "Mi cuenta"
}

@ -36,6 +36,7 @@
"PersonalSettingsSaved": "Ajustes personales guardados",
"PasswordChanged": "Cambiar la contraseña",
"NewPasswordConfirmFailed": "Por favor, confirme la nueva contraseña.",
"NewPasswordSameAsOld": "La nueva contraseña debe ser diferente de la contraseña actual.",
"Manage": "Administrar",
"MyAccount": "Mi cuenta",
"DisplayName:Abp.Account.IsSelfRegistrationEnabled": "¿El auto-registro está habilitado?",

@ -36,6 +36,7 @@
"PersonalSettingsSaved": "تنظیمات شخصی ذخیره گردید",
"PasswordChanged": "گذرواژه تغییر کرد",
"NewPasswordConfirmFailed": "لطفاً گذرواژه جدید را تأیید کنید.",
"NewPasswordSameAsOld": "گذرواژه جدید نباید با گذرواژه قبلی یکسان باشد.",
"Manage": "مدیریت",
"MyAccount": "حساب من",
"DisplayName:Abp.Account.IsSelfRegistrationEnabled": "آیا امکان ثبت نام فعال است",

@ -36,6 +36,7 @@
"PersonalSettingsSaved": "Henkilökohtaiset asetukset tallennettu",
"PasswordChanged": "Salasana vaihdettu",
"NewPasswordConfirmFailed": "Vahvista uusi salasana.",
"NewPasswordSameAsOld": "Uusi salasana ei voi olla sama kuin vanha salasana.",
"Manage": "Hallitse",
"MyAccount": "Tilini",
"DisplayName:Abp.Account.IsSelfRegistrationEnabled": "Onko itserekisteröinti käytössä",

@ -36,6 +36,7 @@
"PersonalSettingsSaved": "Paramètres personnels enregistrés",
"PasswordChanged": "Mot de passe changé",
"NewPasswordConfirmFailed": "Veuillez confirmer le nouveau mot de passe.",
"NewPasswordSameAsOld": "Le nouveau mot de passe doit être différent de l'ancien.",
"Manage": "Gérer",
"MyAccount": "Mon compte",
"DisplayName:Abp.Account.IsSelfRegistrationEnabled": "Lauto-inscription est-elle activée",

@ -36,6 +36,7 @@
"PersonalSettingsSaved": "व्यक्तिगत सेटिंग्स सहेजे गए",
"PasswordChanged": "पासवर्ड बदला गया",
"NewPasswordConfirmFailed": "कृपया नए पासवर्ड की पुष्टि करें।",
"NewPasswordSameAsOld": "नया पासवर्ड वर्तमान पासवर्ड से एक ही होना चाहिए।",
"Manage": "प्रबंधित",
"MyAccount": "मेरा खाता",
"DisplayName:Abp.Account.IsSelfRegistrationEnabled": "क्या स्व-पंजीकरण सक्षम है",

@ -36,6 +36,7 @@
"PersonalSettingsSaved": "Személyes beállítások mentve",
"PasswordChanged": "Jelszó megváltoztatva",
"NewPasswordConfirmFailed": "Kérjük, erősítse meg az új jelszót.",
"NewPasswordSameAsOld": "Az új jelszó nem lehet azonos a régi jelszóval.",
"Manage": "Kezelés",
"MyAccount": "A fiókom",
"DisplayName:Abp.Account.IsSelfRegistrationEnabled": "Engedélyezve van az önregisztráció",

@ -35,6 +35,7 @@
"PersonalSettingsSaved": "Persónulegar stillingar vistaðar",
"PasswordChanged": "Lykilorði breytt",
"NewPasswordConfirmFailed": "Vinsamlegast staðfestu nýja lykilorðið.",
"NewPasswordSameAsOld": "Nýtt lykilorð verður að vera frábrugðið núverandi lykilorði.",
"Manage": "Umsjá",
"MyAccount": "Minn aðgangur",
"DisplayName:Abp.Account.IsSelfRegistrationEnabled": "Er sjálfskráning virk",

@ -36,6 +36,7 @@
"PersonalSettingsSaved": "Impostazioni personali salvate",
"PasswordChanged": "Password cambiata",
"NewPasswordConfirmFailed": "Conferma la nuova password.",
"NewPasswordSameAsOld": "La nuova password deve essere diversa dalla vecchia.",
"Manage": "Gestire",
"MyAccount": "Il mio account",
"DisplayName:Abp.Account.IsSelfRegistrationEnabled": "L'auto-registrazione è abilitata",

@ -35,6 +35,7 @@
"PersonalSettingsSaved": "Persoonlijke instellingen opgeslagen",
"PasswordChanged": "Wachtwoord veranderd",
"NewPasswordConfirmFailed": "Bevestig het nieuwe wachtwoord a.u.b.",
"NewPasswordSameAsOld": "Het nieuwe wachtwoord moet verschillen van het huidige wachtwoord.",
"Manage": "Beheer",
"MyAccount": "Mijn rekening",
"DisplayName:Abp.Account.IsSelfRegistrationEnabled": "Is zelfregistratie ingeschakeld",

@ -35,6 +35,7 @@
"PersonalSettingsSaved": "Zapisano ustawienia osobiste",
"PasswordChanged": "Hasło zostało zmienione",
"NewPasswordConfirmFailed": "Potwierdź nowe hasło.",
"NewPasswordSameAsOld": "Nowe hasło musi być inne niż poprzednie.",
"Manage": "Zarządzać",
"MyAccount": "Moje konto",
"DisplayName:Abp.Account.IsSelfRegistrationEnabled": "Czy włączona jest samodzielna rejestracja?",

@ -36,6 +36,7 @@
"PersonalSettingsSaved": "Configurações pessoais salvas",
"PasswordChanged": "Senha alterada",
"NewPasswordConfirmFailed": "Por favor, confirme a nova senha.",
"NewPasswordSameAsOld": "A nova senha deve ser diferente da senha atual.",
"Manage": "Gerenciar",
"MyAccount": "Minha conta",
"DisplayName:Abp.Account.IsSelfRegistrationEnabled": "O autorregistro está habilitado",

@ -35,6 +35,7 @@
"PersonalSettingsSaved": "Setări personale salvate",
"PasswordChanged": "Parola schimbată",
"NewPasswordConfirmFailed": "Vă rugăm să confirmaţi noua parolă.",
"NewPasswordSameAsOld": "Noua parolă trebuie să fie diferită de cea curentă.",
"Manage": "Administrează",
"MyAccount": "Contul meu",
"DisplayName:Abp.Account.IsSelfRegistrationEnabled": "Este înregistrarea personală activată",

@ -35,6 +35,7 @@
"PersonalSettingsSaved": "Персональные настройки сохранены",
"PasswordChanged": "Пароль изменен",
"NewPasswordConfirmFailed": "Пожалуйста подтвердите новый пароль.",
"NewPasswordSameAsOld": "Новый пароль не должен совпадать со старым.",
"Manage": "Управление учетной записью",
"MyAccount": "Мой аккаунт",
"DisplayName:Abp.Account.IsSelfRegistrationEnabled": "Самостоятельная регистрация включена",

@ -36,6 +36,7 @@
"PersonalSettingsSaved": "Osobné nastavenia boli uložené",
"PasswordChanged": "Heslo bolo zmenené",
"NewPasswordConfirmFailed": "Prosím potvrďte nové heslo.",
"NewPasswordSameAsOld": "Nové heslo musí byť odlišné od aktuálneho.",
"Manage": "Spravovať",
"MyAccount": "Môj účet",
"DisplayName:Abp.Account.IsSelfRegistrationEnabled": "Je povolená samoregistrácia",

@ -36,6 +36,7 @@
"PersonalSettingsSaved": "Osebne nastavitve so shranjene",
"PasswordChanged": "Geslo je spremenjeno",
"NewPasswordConfirmFailed": "Prosimo potrdite novo geslo.",
"NewPasswordSameAsOld": "Novo geslo mora biti drugačno od starega.",
"Manage": "Upravljaj",
"MyAccount": "Moj račun",
"DisplayName:Abp.Account.IsSelfRegistrationEnabled": "Je lastna registracija uporabnika omogočena",

@ -36,6 +36,7 @@
"PersonalSettingsSaved": "Kişisel ayarlar kaydedildi",
"PasswordChanged": "Şifre değiştirildi",
"NewPasswordConfirmFailed": "Lütfen yeni şifreyi onaylayın.",
"NewPasswordSameAsOld": "Yeni şifre eski şifre ile aynı olamaz.",
"Manage": "Manage",
"MyAccount": "Hesabım",
"DisplayName:Abp.Account.IsSelfRegistrationEnabled": "self-registration etkin mi ?",

@ -35,6 +35,7 @@
"PersonalSettingsSaved": "Đã lưu cài đặt cá nhân",
"PasswordChanged": "mật khẩu đã được thay đổi",
"NewPasswordConfirmFailed": "Vui lòng xác nhận mật khẩu mới.",
"NewPasswordSameAsOld": "Mật khẩu mới không được giống như mật khẩu cũ.",
"Manage": "Quản lý",
"MyAccount": "Tài khoản của tôi",
"DisplayName:Abp.Account.IsSelfRegistrationEnabled": "Tự đăng ký có được bật không",

@ -36,6 +36,7 @@
"PersonalSettingsSaved": "个人设置已保存",
"PasswordChanged": "修改密码",
"NewPasswordConfirmFailed": "请确认新密码",
"NewPasswordSameAsOld": "新密码不能与旧密码相同",
"Manage": "管理",
"MyAccount": "我的账户",
"DisplayName:Abp.Account.IsSelfRegistrationEnabled": "启用自行注册",

@ -36,6 +36,7 @@
"PersonalSettingsSaved": "個人設置已保存",
"PasswordChanged": "修改密碼",
"NewPasswordConfirmFailed": "請確認新密碼",
"NewPasswordSameAsOld": "新密碼不能與舊密碼相同",
"Manage": "管理",
"MyAccount": "我的賬戶",
"DisplayName:Abp.Account.IsSelfRegistrationEnabled": "啟用自行註冊",

@ -22,7 +22,12 @@
if (input.currentPassword && input.currentPassword == ''){
return;
}
if(input.currentPassword == input.newPassword) {
abp.message.error(l('NewPasswordSameAsOld'));
return;
}
volo.abp.account.profile.changePassword(input).then(function (result) {
abp.message.success(l('PasswordChanged'));
abp.event.trigger('passwordChanged');

@ -93,7 +93,5 @@ public class PageAdminAppService : CmsKitAdminAppServiceBase, IPageAdminAppServi
var page = await PageRepository.GetAsync(id);
await PageManager.SetHomePageAsync(page);
await PageRepository.UpdateAsync(page);
}
}

@ -56,7 +56,8 @@ public class PageManager : DomainService
}
await PageRepository.UpdateManyAsync(currentHomePages);
await PageRepository.UpdateAsync(page);
page.SetIsHomePage(true);
}

@ -116,6 +116,10 @@ public class CmsKitPublicWebModule : AbpModule
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
var app = context.GetApplicationBuilder();
app.UseHomePageDefaultMiddleware();
if (GlobalFeatureManager.Instance.IsEnabled<PagesFeature>())
{
app.UseHomePageDefaultMiddleware();
}
}
}

@ -72,12 +72,14 @@ public class PageManager_Test : CmsKitDomainTestBase
[Fact]
public async Task SetHomePageAsync_ShouldWorkProperly_IfExistHomePage()
{
var page = await pageRepository.GetAsync(testData.Page_1_Id);
await pageManager.SetHomePageAsync(page);
await WithUnitOfWorkAsync(async ()=>
{
var page = await pageRepository.GetAsync(testData.Page_1_Id);
await pageRepository.UpdateAsync(page);
await pageManager.SetHomePageAsync(page);
});
var page = await pageRepository.GetAsync(testData.Page_1_Id);
page.IsHomePage.ShouldBeTrue();
var pageSetAsHomePageAsFalse = await pageRepository.GetAsync(testData.Page_2_Id);

@ -47,7 +47,7 @@
<Validations @ref="@CreateValidationsRef" Model="@NewEntity" ValidateOnLoad="false">
<Validation MessageLocalizer="@LH.Localize">
<Field>
<FieldLabel>@L["DisplayName:RoleName"]</FieldLabel>
<FieldLabel>@L["DisplayName:RoleName"] *</FieldLabel>
<TextEdit @bind-Text="@NewEntity.Name" Autofocus="true">
<Feedback>
<ValidationError/>
@ -85,7 +85,7 @@
<input type="hidden" name="ConcurrencyStamp" @bind-value="EditingEntity.ConcurrencyStamp"/>
<Validation MessageLocalizer="@LH.Localize">
<Field>
<FieldLabel>@L["DisplayName:RoleName"]</FieldLabel>
<FieldLabel>@L["DisplayName:RoleName"] *</FieldLabel>
<TextEdit @bind-Text="EditingEntity.Name" Autofocus="true">
<Feedback>
<ValidationError/>

@ -64,7 +64,7 @@
<TabPanel Name="UserInformations">
<Validation MessageLocalizer="@LH.Localize">
<Field>
<FieldLabel>@L["DisplayName:UserName"]</FieldLabel>
<FieldLabel>@L["DisplayName:UserName"] *</FieldLabel>
<TextEdit @bind-Text="NewEntity.UserName" Autofocus="true">
<Feedback>
<ValidationError/>
@ -94,7 +94,7 @@
</Validation>
<Validation MessageLocalizer="@LH.Localize">
<Field>
<FieldLabel>@L["DisplayName:Password"]</FieldLabel>
<FieldLabel>@L["DisplayName:Password"] *</FieldLabel>
<Addons>
<Addon AddonType="AddonType.Body">
<TextEdit Role="@_passwordTextRole" @bind-Text="NewEntity.Password">
@ -109,7 +109,7 @@
</Validation>
<Validation MessageLocalizer="@LH.Localize">
<Field>
<FieldLabel>@L["DisplayName:Email"]</FieldLabel>
<FieldLabel>@L["DisplayName:Email"] *</FieldLabel>
<TextEdit @bind-Text="NewEntity.Email">
<Feedback>
<ValidationError/>
@ -183,7 +183,7 @@
<TabPanel Name="UserInformations">
<Validation MessageLocalizer="@LH.Localize">
<Field>
<FieldLabel>@L["DisplayName:UserName"]</FieldLabel>
<FieldLabel>@L["DisplayName:UserName"] *</FieldLabel>
<TextEdit @bind-Text="EditingEntity.UserName" Autofocus="true">
<Feedback>
<ValidationError/>
@ -228,7 +228,7 @@
</Validation>
<Validation MessageLocalizer="@LH.Localize">
<Field>
<FieldLabel>@L["DisplayName:Email"]</FieldLabel>
<FieldLabel>@L["DisplayName:Email"] *</FieldLabel>
<TextEdit @bind-Text="EditingEntity.Email">
<Feedback>
<ValidationError/>

@ -25,6 +25,7 @@ public static class OpenIddictApplicationExtensions
foreach (var extraProperty in model.ExtraProperties)
{
entity.ExtraProperties.Remove(extraProperty.Key);
entity.ExtraProperties.Add(extraProperty.Key, extraProperty.Value);
}
@ -86,6 +87,7 @@ public static class OpenIddictApplicationExtensions
foreach (var extraProperty in entity.ExtraProperties)
{
model.ExtraProperties.Remove(extraProperty.Key);
model.ExtraProperties.Add(extraProperty.Key, extraProperty.Value);
}

@ -19,6 +19,7 @@ public static class OpenIddictAuthorizationExtensions
foreach (var extraProperty in model.ExtraProperties)
{
entity.ExtraProperties.Remove(extraProperty.Key);
entity.ExtraProperties.Add(extraProperty.Key, extraProperty.Value);
}
@ -68,6 +69,7 @@ public static class OpenIddictAuthorizationExtensions
foreach (var extraProperty in entity.ExtraProperties)
{
model.ExtraProperties.Remove(extraProperty.Key);
model.ExtraProperties.Add(extraProperty.Key, extraProperty.Value);
}

@ -19,6 +19,7 @@ public static class OpenIddictScopeExtensions
foreach (var extraProperty in model.ExtraProperties)
{
entity.ExtraProperties.Remove(extraProperty.Key);
entity.ExtraProperties.Add(extraProperty.Key, extraProperty.Value);
}
@ -69,6 +70,7 @@ public static class OpenIddictScopeExtensions
foreach (var extraProperty in entity.ExtraProperties)
{
model.ExtraProperties.Remove(extraProperty.Key);
model.ExtraProperties.Add(extraProperty.Key, extraProperty.Value);
}

@ -24,6 +24,7 @@ public static class OpenIddictTokenExtensions
foreach (var extraProperty in model.ExtraProperties)
{
entity.ExtraProperties.Remove(extraProperty.Key);
entity.ExtraProperties.Add(extraProperty.Key, extraProperty.Value);
}
@ -81,6 +82,7 @@ public static class OpenIddictTokenExtensions
foreach (var extraProperty in entity.ExtraProperties)
{
model.ExtraProperties.Remove(extraProperty.Key);
model.ExtraProperties.Add(extraProperty.Key, extraProperty.Value);
}

@ -46,7 +46,7 @@
<Validations @ref="@CreateValidationsRef" Model="@NewEntity" ValidateOnLoad="false">
<Validation MessageLocalizer="@LH.Localize">
<Field>
<FieldLabel>@L["TenantName"]</FieldLabel>
<FieldLabel>@L["TenantName"] *</FieldLabel>
<TextEdit @bind-Text="@NewEntity.Name" Autofocus="true">
<Feedback>
<ValidationError/>
@ -56,7 +56,7 @@
</Validation>
<Validation MessageLocalizer="@LH.Localize">
<Field>
<FieldLabel>@L["DisplayName:AdminEmailAddress"]</FieldLabel>
<FieldLabel>@L["DisplayName:AdminEmailAddress"] *</FieldLabel>
<TextEdit Role="@TextRole.Email" @bind-Text="@NewEntity.AdminEmailAddress">
<Feedback>
<ValidationError/>
@ -66,7 +66,7 @@
</Validation>
<Validation MessageLocalizer="@LH.Localize">
<Field>
<FieldLabel>@L["DisplayName:AdminPassword"]</FieldLabel>
<FieldLabel>@L["DisplayName:AdminPassword"] *</FieldLabel>
<TextEdit Role="@TextRole.Password" @bind-Text="@NewEntity.AdminPassword">
<Feedback>
<ValidationError/>
@ -100,7 +100,7 @@
<Validations @ref="@EditValidationsRef" Model="@EditingEntity" ValidateOnLoad="false">
<Validation MessageLocalizer="@LH.Localize">
<Field>
<FieldLabel>@L["TenantName"]</FieldLabel>
<FieldLabel>@L["TenantName"] *</FieldLabel>
<TextEdit @bind-Text="@EditingEntity.Name" Autofocus="true">
<Feedback>
<ValidationError/>

@ -13,6 +13,7 @@ import { environment } from '../environments/environment';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { APP_ROUTE_PROVIDER } from './route.provider';
import { FeatureManagementModule } from '@abp/ng.feature-management';
@NgModule({
imports: [
@ -29,6 +30,7 @@ import { APP_ROUTE_PROVIDER } from './route.provider';
AccountConfigModule.forRoot(),
IdentityConfigModule.forRoot(),
TenantManagementConfigModule.forRoot(),
FeatureManagementModule.forRoot(),
SettingManagementConfigModule.forRoot(),
ThemeBasicModule.forRoot(),
],

@ -0,0 +1,19 @@
<button class="btn btn-primary" type="button" (click)="openFeaturesModal()">
<i class="fa fa-cog"></i>
{{ 'FeatureManagement::ManageHostFeatures' | abpLocalization }}
</button>
<abp-feature-management
*abpReplaceableTemplate="{
inputs: {
providerName: { value: 'T' },
providerKey: { value: providerKey },
visible: { value: visibleFeatures, twoWay: true }
},
outputs: { visibleChange: onVisibleFeaturesChange },
componentKey: 'FeatureManagement.FeatureManagementComponent'
}"
[(visible)]="visibleFeatures"
providerName="T"
[providerKey]="providerKey"
>
</abp-feature-management>

@ -0,0 +1,20 @@
import { Component } from '@angular/core';
@Component({
selector: 'abp-feature-management-tab',
templateUrl: './feature-management-tab.component.html',
})
export class FeatureManagementTabComponent {
visibleFeatures = false;
providerKey: string;
openFeaturesModal() {
setTimeout(() => {
this.visibleFeatures = true;
}, 0);
}
onVisibleFeaturesChange = (value: boolean) => {
this.visibleFeatures = value;
};
}

@ -1 +1,2 @@
export * from './feature-management/feature-management.component';
export * from './feature-management-tab/feature-management-tab.component';

@ -0,0 +1,3 @@
export const enum eFeatureManagementTabNames {
FeatureManagement = 'AbpFeatureManagement::Permission:FeatureManagement',
}

@ -0,0 +1,2 @@
export * from './feature-management-tab-names';
export * from './components';

@ -1,15 +1,28 @@
import { CoreModule } from '@abp/ng.core';
import { ThemeSharedModule } from '@abp/ng.theme.shared';
import { NgModule } from '@angular/core';
import { ModuleWithProviders, NgModule } from '@angular/core';
import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap';
import { FeatureManagementComponent } from './components/feature-management/feature-management.component';
import { FreeTextInputDirective } from './directives/free-text-input.directive';
import { FEATURE_MANAGEMENT_SETTINGS_PROVIDERS } from './providers';
import { FeatureManagementTabComponent } from './components';
const exported = [FeatureManagementComponent, FreeTextInputDirective];
const exported = [
FeatureManagementComponent,
FreeTextInputDirective,
FeatureManagementTabComponent,
];
@NgModule({
declarations: [...exported],
imports: [CoreModule, ThemeSharedModule, NgbNavModule],
exports: [...exported],
})
export class FeatureManagementModule {}
export class FeatureManagementModule {
static forRoot(): ModuleWithProviders<FeatureManagementModule> {
return {
ngModule: FeatureManagementModule,
providers: [FEATURE_MANAGEMENT_SETTINGS_PROVIDERS],
};
}
}

@ -0,0 +1,26 @@
import { SettingTabsService } from '@abp/ng.setting-management/config';
import { APP_INITIALIZER } from '@angular/core';
import { eFeatureManagementTabNames } from '../enums/feature-management-tab-names';
import { FeatureManagementTabComponent } from '../components';
export const FEATURE_MANAGEMENT_SETTINGS_PROVIDERS = [
{
provide: APP_INITIALIZER,
useFactory: configureSettingTabs,
deps: [SettingTabsService],
multi: true,
},
];
export function configureSettingTabs(settingtabs: SettingTabsService) {
return () => {
settingtabs.add([
{
name: eFeatureManagementTabNames.FeatureManagement,
order: 104,
requiredPolicy: 'FeatureManagement.ManageHostFeatures',
component: FeatureManagementTabComponent,
},
]);
};
}

@ -0,0 +1 @@
export * from './feature-management-settings.provider';

@ -1,5 +1,6 @@
export * from './lib/components';
export * from './lib/directives';
export * from './lib/providers';
export * from './lib/enums/components';
export * from './lib/feature-management.module';
export * from './lib/models';
export * from './lib/models';

@ -1,17 +1,13 @@
<ng-container [ngSwitch]="getComponent(prop)"
*abpPermission="prop.permission; runChangeDetection: false">
<ng-container
[ngSwitch]="getComponent(prop)"
*abpPermission="prop.permission; runChangeDetection: false"
>
<ng-template ngSwitchCase="template">
<ng-container
*ngComponentOutlet="prop.template;injector:injectorForCustomComponent">
<ng-container *ngComponentOutlet="prop.template; injector: injectorForCustomComponent">
</ng-container>
</ng-template>
<div
[ngClass]="containerClassName"
class="mb-3 form-group"
>
<div [ngClass]="containerClassName" class="mb-3 form-group">
<ng-template ngSwitchCase="input">
<ng-template [ngTemplateOutlet]="label"></ng-template>
<input
@ -27,7 +23,7 @@
</ng-template>
<ng-template ngSwitchCase="hidden">
<input [formControlName]="prop.name" type="hidden"/>
<input [formControlName]="prop.name" type="hidden" />
</ng-template>
<ng-template ngSwitchCase="checkbox">
@ -103,7 +99,7 @@
[class.is-invalid]="typeahead.classList.contains('is-invalid')"
class="form-control"
/>
<input [formControlName]="prop.name" type="hidden"/>
<input [formControlName]="prop.name" type="hidden" />
</div>
</ng-template>
@ -143,11 +139,14 @@
></textarea>
</ng-template>
</div>
</ng-container>
<ng-template #label let-classes>
<label [htmlFor]="prop.id" [ngClass]="classes || 'form-label'" x
>{{ prop.displayName | abpLocalization }} {{ asterisk }}</label
>
<label [htmlFor]="prop.id" [ngClass]="classes || 'form-label'">
<ng-container *ngIf="prop.displayTextResolver; else displayNameTemplate">
{{ prop.displayTextResolver(data) | abpLocalization }}
</ng-container>
<ng-template #displayNameTemplate> {{ prop.displayName | abpLocalization }}</ng-template>
{{ asterisk }}
</label>
</ng-template>

@ -60,7 +60,7 @@ export class ExtensibleFormPropComponent implements OnChanges, AfterViewInit {
asterisk = '';
containerClassName = 'mb-3 form-group'
containerClassName = 'mb-3 form-group';
options$: Observable<ABP.Option<any>[]> = of([]);
@ -136,6 +136,7 @@ export class ExtensibleFormPropComponent implements OnChanges, AfterViewInit {
ngAfterViewInit() {
if (this.first && this.fieldRef) {
this.fieldRef.nativeElement.focus();
this.cdRef.detectChanges();
}
}
@ -213,7 +214,7 @@ export class ExtensibleFormPropComponent implements OnChanges, AfterViewInit {
this.validators = validators(this.data);
this.setAsterisk();
}
if(className !== undefined){
if (className !== undefined) {
this.containerClassName = className;
}

@ -1,4 +1,10 @@
<ngx-datatable default [rows]="data" [count]="recordsTotal" [list]="list">
<ngx-datatable
default
[rows]="data"
[count]="recordsTotal"
[list]="list"
(activate)="tableActivate.emit($event)"
>
<ngx-datatable-column
*ngIf="actionsTemplate || (actionList.length && hasAtLeastOnePermittedAction)"
[name]="actionsText | abpLocalization"

@ -11,6 +11,7 @@ import { formatDate } from '@angular/common';
import {
ChangeDetectionStrategy,
Component,
EventEmitter,
Inject,
InjectFlags,
InjectionToken,
@ -18,6 +19,7 @@ import {
Input,
LOCALE_ID,
OnChanges,
Output,
SimpleChanges,
TemplateRef,
TrackByFunction,
@ -63,6 +65,8 @@ export class ExtensibleTableComponent<R = any> implements OnChanges {
}
@Input() actionsTemplate: TemplateRef<any>;
@Output() tableActivate = new EventEmitter();
getInjected: <T>(token: Type<T> | InjectionToken<T>, notFoundValue?: T, flags?: InjectFlags) => T;
hasAtLeastOnePermittedAction: boolean;
@ -158,7 +162,6 @@ export class ExtensibleTableComponent<R = any> implements OnChanges {
value,
};
if (prop.value.component) {
const injector = Injector.create({
providers: [
{

@ -9,6 +9,7 @@ import {
PropContributorCallback,
PropContributorCallbacks,
PropData,
PropDisplayTextResolver,
PropList,
PropPredicate,
Props,
@ -69,6 +70,7 @@ export class FormProp<R = any> extends Prop<R> {
readonly template?: Type<any>;
readonly className?: string;
readonly group?: FormPropGroup | undefined;
readonly displayTextResolver?: PropDisplayTextResolver<R>;
constructor(options: FormPropOptions<R>) {
super(
@ -93,6 +95,7 @@ export class FormProp<R = any> extends Prop<R> {
this.id = options.id || options.name;
const defaultValue = options.defaultValue;
this.defaultValue = isFalsyValue(defaultValue) ? defaultValue : defaultValue || null;
this.displayTextResolver = options.displayTextResolver;
}
static create<R = any>(options: FormPropOptions<R>) {
@ -128,6 +131,7 @@ export type FormPropOptions<R = any> = O.Optional<
| 'defaultValue'
| 'options'
| 'id'
| 'displayTextResolver'
>;
export type CreateFormPropDefaults<R = any> = Record<string, FormProp<R>[]>;

@ -35,6 +35,7 @@ export abstract class Prop<R = any> {
public readonly isExtra = false,
public readonly template?: Type<any>,
public readonly className?: string,
public readonly displayTextResolver?: PropDisplayTextResolver<R>,
) {
this.displayName = this.displayName || this.name;
}
@ -42,6 +43,7 @@ export abstract class Prop<R = any> {
export type PropCallback<T, R = any> = (data: Omit<PropData<T>, 'data'>, auxData?: any) => R;
export type PropPredicate<T> = (data?: Omit<PropData<T>, 'data'>, auxData?: any) => boolean;
export type PropDisplayTextResolver<T> = (data?: Omit<PropData<T>, 'data'>) => string;
export abstract class PropsFactory<C extends Props<any>> {
protected abstract _ctor: Type<C>;

@ -204,12 +204,10 @@ var abp = abp || {};
abp.auth = abp.auth || {};
abp.auth.policies = abp.auth.policies || {};
abp.auth.grantedPolicies = abp.auth.grantedPolicies || {};
abp.auth.isGranted = function (policyName) {
return abp.auth.policies[policyName] != undefined && abp.auth.grantedPolicies[policyName] != undefined;
return abp.auth.grantedPolicies[policyName] != undefined;
};
abp.auth.isAnyGranted = function () {

@ -14,6 +14,7 @@ import { environment } from '../environments/environment';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { APP_ROUTE_PROVIDER } from './route.provider';
import { FeatureManagementModule } from '@abp/ng.feature-management';
@NgModule({
imports: [
@ -31,6 +32,7 @@ import { APP_ROUTE_PROVIDER } from './route.provider';
SettingManagementConfigModule.forRoot(),
ThemeLeptonXModule.forRoot(),
SideMenuLayoutModule.forRoot(),
FeatureManagementModule.forRoot(),
],
declarations: [AppComponent],
providers: [APP_ROUTE_PROVIDER],

@ -14,6 +14,7 @@ import { environment } from '../environments/environment';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { APP_ROUTE_PROVIDER } from './route.provider';
import { FeatureManagementModule } from '@abp/ng.feature-management';
@NgModule({
imports: [
@ -31,6 +32,7 @@ import { APP_ROUTE_PROVIDER } from './route.provider';
SettingManagementConfigModule.forRoot(),
ThemeLeptonXModule.forRoot(),
SideMenuLayoutModule.forRoot(),
FeatureManagementModule.forRoot(),
],
declarations: [AppComponent],
providers: [APP_ROUTE_PROVIDER],

@ -14,6 +14,7 @@ import { environment } from '../environments/environment';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { APP_ROUTE_PROVIDER } from './route.provider';
import { FeatureManagementModule } from '@abp/ng.feature-management';
@NgModule({
imports: [
@ -33,6 +34,7 @@ import { APP_ROUTE_PROVIDER } from './route.provider';
SettingManagementConfigModule.forRoot(),
MyProjectNameConfigModule.forRoot(),
ThemeBasicModule.forRoot(),
FeatureManagementModule.forRoot(),
],
providers: [APP_ROUTE_PROVIDER],
declarations: [AppComponent],

Loading…
Cancel
Save