chore: resolve conflict with dev branch

pull/6175/head
bnymncoskuner 5 years ago
commit ff289bcaea

@ -99,10 +99,11 @@ This is a minimalist, empty project template that is integrated to the ABP Frame
### New Languages
**Thanks to the contributors** from the ABP Community, the framework modules and the startup template have been localized to **German** (by [Alexander Pilhar](https://github.com/alexanderpilhar) & [Nico Lachmuth](https://github.com/tntwist)) and **Spanish** (by [Jose Manuel Gonzalez](https://github.com/jmglezgz)) languages.
**Thanks to the contributors** from the ABP Community, the framework modules and the startup template have been localized to **German** language by [Alexander Pilhar](https://github.com/alexanderpilhar) & [Nico Lachmuth](https://github.com/tntwist).
### Other Notes
* Upgraded to Angular 11.
* Since [Mongo2Go](https://github.com/Mongo2Go/Mongo2Go) library not supports transactions, you can use transactions in unit tests for MongoDB.
## What's new with the ABP Commercial 4.0

@ -38,31 +38,37 @@ namespace Acme.BookStore
{
private readonly IRepository<Book, Guid> _bookRepository;
private readonly IGuidGenerator _guidGenerator;
private readonly ICurrentTenant _currentTenant;
public BookStoreDataSeedContributor(
IRepository<Book, Guid> bookRepository,
IGuidGenerator guidGenerator)
IGuidGenerator guidGenerator,
ICurrentTenant currentTenant)
{
_bookRepository = bookRepository;
_guidGenerator = guidGenerator;
_currentTenant = currentTenant;
}
public async Task SeedAsync(DataSeedContext context)
{
if (await _bookRepository.GetCountAsync() > 0)
using (_currentTenant.Change(context?.TenantId))
{
return;
if (await _bookRepository.GetCountAsync() > 0)
{
return;
}
var book = new Book(
id: _guidGenerator.Create(),
name: "The Hitchhiker's Guide to the Galaxy",
type: BookType.ScienceFiction,
publishDate: new DateTime(1979, 10, 12),
price: 42
);
await _bookRepository.InsertAsync(book);
}
var book = new Book(
id: _guidGenerator.Create(),
name: "The Hitchhiker's Guide to the Galaxy",
type: BookType.ScienceFiction,
publishDate: new DateTime(1979, 10, 12),
price: 42
);
await _bookRepository.InsertAsync(book);
}
}
}

@ -38,31 +38,37 @@ namespace Acme.BookStore
{
private readonly IRepository<Book, Guid> _bookRepository;
private readonly IGuidGenerator _guidGenerator;
private readonly ICurrentTenant _currentTenant;
public BookStoreDataSeedContributor(
IRepository<Book, Guid> bookRepository,
IGuidGenerator guidGenerator)
IGuidGenerator guidGenerator,
ICurrentTenant currentTenant)
{
_bookRepository = bookRepository;
_guidGenerator = guidGenerator;
_currentTenant = currentTenant;
}
public async Task SeedAsync(DataSeedContext context)
{
if (await _bookRepository.GetCountAsync() > 0)
using (_currentTenant.Change(context?.TenantId))
{
return;
if (await _bookRepository.GetCountAsync() > 0)
{
return;
}
var book = new Book(
id: _guidGenerator.Create(),
name: "The Hitchhiker's Guide to the Galaxy",
type: BookType.ScienceFiction,
publishDate: new DateTime(1979, 10, 12),
price: 42
);
await _bookRepository.InsertAsync(book);
}
var book = new Book(
id: _guidGenerator.Create(),
name: "The Hitchhiker's Guide to the Galaxy",
type: BookType.ScienceFiction,
publishDate: new DateTime(1979, 10, 12),
price: 42
);
await _bookRepository.InsertAsync(book);
}
}
}

@ -9,5 +9,6 @@ namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending
public string ResultListPropertyName { get; set; }
public string DisplayPropertyName { get; set; }
public string ValuePropertyName { get; set; }
public string FilterParamName { get; set; }
}
}

@ -29,6 +29,8 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form
public string AutocompleteValuePropertyName { get; set; }
public string AutocompleteFilterParamName { get; set; }
public AbpSelectTagHelper(AbpSelectTagHelperService tagHelperService)
: base(tagHelperService)
{

@ -106,6 +106,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form
output.Attributes.Add("data-autocomplete-items-property", TagHelper.AutocompleteItemsPropertyName);
output.Attributes.Add("data-autocomplete-display-property", TagHelper.AutocompleteDisplayPropertyName);
output.Attributes.Add("data-autocomplete-value-property", TagHelper.AutocompleteValuePropertyName);
output.Attributes.Add("data-autocomplete-filter-param-name", TagHelper.AutocompleteFilterParamName);
}
}

@ -79,14 +79,14 @@
var displayName = $(this).data("autocompleteDisplayProperty");
var displayValue = $(this).data("autocompleteValueProperty");
var itemsPropertyName = $(this).data("autocompleteItemsProperty");
var filterParamName = $(this).data("autocompleteFilterParamName");
$select.select2({
ajax: {
url: url,
dataType: "json",
data: function (params) {
var query = {
search: params.term
};
var query = {};
query[filterParamName] = params.term;
return query;
},
processResults: function (data) {
@ -95,7 +95,7 @@
if (itemsPropertyName) {
items = data[itemsPropertyName];
}
items.forEach(function (item, index) {
retVal.push({
id: item[displayValue],

@ -148,7 +148,8 @@ namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending
Url = propertyConfig.UI.Lookup.Url,
ResultListPropertyName = propertyConfig.UI.Lookup.ResultListPropertyName,
DisplayPropertyName = propertyConfig.UI.Lookup.DisplayPropertyName,
ValuePropertyName = propertyConfig.UI.Lookup.ValuePropertyName
ValuePropertyName = propertyConfig.UI.Lookup.ValuePropertyName,
FilterParamName = propertyConfig.UI.Lookup.FilterParamName
}
}
};

@ -29,7 +29,7 @@ namespace Volo.Abp.AspNetCore.Mvc.Json
options.JsonSerializerOptions.Converters.Add(new AbpStringToBooleanConverter());
options.JsonSerializerOptions.Converters.Add(new ObjectToInferredTypesConverter());
options.JsonSerializerOptions.Converters.Add(new AbpExtraPropertyDictionaryJsonConverterFactory());
options.JsonSerializerOptions.Converters.Add(new AbpHasExtraPropertiesJsonConverterFactory());
}
}
}

@ -23,7 +23,7 @@ namespace Volo.Abp.Json.SystemTextJson
options.JsonSerializerOptions.Converters.Add(new AbpStringToBooleanConverter());
options.JsonSerializerOptions.Converters.Add(new ObjectToInferredTypesConverter());
options.JsonSerializerOptions.Converters.Add(new AbpExtraPropertyDictionaryJsonConverterFactory());
options.JsonSerializerOptions.Converters.Add(new AbpHasExtraPropertiesJsonConverterFactory());
}
}
}

@ -1,26 +0,0 @@
using System;
using System.Reflection;
using System.Text.Json;
using System.Text.Json.Serialization;
using Volo.Abp.ObjectExtending;
namespace Volo.Abp.Json.SystemTextJson.JsonConverters
{
public class AbpExtraPropertyDictionaryJsonConverterFactory : JsonConverterFactory
{
public override bool CanConvert(Type typeToConvert)
{
return typeToConvert == typeof(ExtensibleObject) || typeToConvert.IsSubclassOf(typeof(ExtensibleObject));
}
public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options)
{
return (JsonConverter) Activator.CreateInstance(
typeof(AbpExtraPropertyDictionaryJsonConverter<>).MakeGenericType(typeToConvert),
BindingFlags.Instance | BindingFlags.Public,
binder: null,
null,
culture: null)!;
}
}
}

@ -1,30 +1,27 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Text.Json.Serialization;
using Volo.Abp.Data;
using Volo.Abp.ObjectExtending;
namespace Volo.Abp.Json.SystemTextJson.JsonConverters
{
public class AbpExtraPropertyDictionaryJsonConverter<T> : JsonConverter<T>
where T : ExtensibleObject
public class AbpHasExtraPropertiesJsonConverter<T> : JsonConverter<T>
where T : IHasExtraProperties
{
public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
var newOptions = JsonSerializerOptionsHelper.Create(options, x =>
x == this ||
x.GetType() == typeof(AbpExtraPropertyDictionaryJsonConverterFactory));
x.GetType() == typeof(AbpHasExtraPropertiesJsonConverterFactory));
var rootElement = JsonDocument.ParseValue(ref reader).RootElement;
var extensibleObject = JsonSerializer.Deserialize<T>(rootElement.GetRawText(), newOptions);
var extraProperties = rootElement.EnumerateObject().FirstOrDefault(x =>
x.Name.Equals(nameof(ExtensibleObject.ExtraProperties), StringComparison.OrdinalIgnoreCase))
x.Name.Equals(nameof(IHasExtraProperties.ExtraProperties), StringComparison.OrdinalIgnoreCase))
.Value.GetRawText();
var extraPropertyDictionary = JsonSerializer.Deserialize(extraProperties, typeof(ExtraPropertyDictionary), newOptions);
ObjectHelper.TrySetProperty(extensibleObject, x => x.ExtraProperties, () => extraPropertyDictionary);
return extensibleObject;
@ -34,7 +31,7 @@ namespace Volo.Abp.Json.SystemTextJson.JsonConverters
{
var newOptions = JsonSerializerOptionsHelper.Create(options, x =>
x == this ||
x.GetType() == typeof(AbpExtraPropertyDictionaryJsonConverterFactory));
x.GetType() == typeof(AbpHasExtraPropertiesJsonConverterFactory));
JsonSerializer.Serialize(writer, value, newOptions);
}

@ -0,0 +1,45 @@
using System;
using System.Collections.Concurrent;
using System.Reflection;
using System.Text.Json;
using System.Text.Json.Serialization;
using Volo.Abp.Data;
namespace Volo.Abp.Json.SystemTextJson.JsonConverters
{
public class AbpHasExtraPropertiesJsonConverterFactory : JsonConverterFactory
{
private static readonly ConcurrentDictionary<Type, bool> CachedTypes = new ConcurrentDictionary<Type, bool>();
public override bool CanConvert(Type typeToConvert)
{
//Only for private or protected ExtraProperties.
if (typeof(IHasExtraProperties).IsAssignableFrom(typeToConvert))
{
return CachedTypes.GetOrAdd(typeToConvert, type =>
{
var property = type.GetProperty(nameof(IHasExtraProperties.ExtraProperties));
if (property != null)
{
var setMethod = property.GetSetMethod(true);
return setMethod != null && (setMethod.IsPrivate || setMethod.IsFamily);
}
return false;
});
}
return false;
}
public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options)
{
return (JsonConverter) Activator.CreateInstance(
typeof(AbpHasExtraPropertiesJsonConverter<>).MakeGenericType(typeToConvert),
BindingFlags.Instance | BindingFlags.Public,
binder: null,
null,
culture: null)!;
}
}
}

@ -1,4 +1,6 @@
namespace Volo.Abp.ObjectExtending.Modularity
using Microsoft.Extensions.Logging;
namespace Volo.Abp.ObjectExtending.Modularity
{
public class ExtensionPropertyLookupConfiguration
{
@ -6,5 +8,6 @@
public string ResultListPropertyName { get; set; } = "items";
public string DisplayPropertyName { get; set; } = "text";
public string ValuePropertyName { get; set; } = "id";
public string FilterParamName { get; set; } = "filter";
}
}

@ -2,6 +2,7 @@
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Guids;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Users;
using Volo.CmsKit.Comments;
using Volo.CmsKit.Ratings;
@ -18,6 +19,7 @@ namespace Volo.CmsKit
private readonly ICommentRepository _commentRepository;
private readonly ReactionManager _reactionManager;
private readonly IRatingRepository _ratingRepository;
private readonly ICurrentTenant _currentTenant;
public CmsKitDataSeedContributor(
IGuidGenerator guidGenerator,
@ -25,7 +27,8 @@ namespace Volo.CmsKit
CmsKitTestData cmsKitTestData,
ICommentRepository commentRepository,
ReactionManager reactionManager,
IRatingRepository ratingRepository)
IRatingRepository ratingRepository,
ICurrentTenant currentTenant)
{
_guidGenerator = guidGenerator;
_cmsUserRepository = cmsUserRepository;
@ -33,17 +36,21 @@ namespace Volo.CmsKit
_commentRepository = commentRepository;
_reactionManager = reactionManager;
_ratingRepository = ratingRepository;
_currentTenant = currentTenant;
}
public async Task SeedAsync(DataSeedContext context)
{
await SeedUsersAsync();
using (_currentTenant.Change(context?.TenantId))
{
await SeedUsersAsync();
await SeedCommentsAsync();
await SeedCommentsAsync();
await SeedReactionsAsync();
await SeedReactionsAsync();
await SeedRatingsAsync();
await SeedRatingsAsync();
}
}
private async Task SeedUsersAsync()
@ -148,21 +155,21 @@ namespace Volo.CmsKit
4,
_cmsKitTestData.User1Id
));
await _ratingRepository.InsertAsync(new Rating(_guidGenerator.Create(),
_cmsKitTestData.EntityType1,
_cmsKitTestData.EntityId1,
5,
_cmsKitTestData.User1Id
));
await _ratingRepository.InsertAsync(new Rating(_guidGenerator.Create(),
_cmsKitTestData.EntityType2,
_cmsKitTestData.EntityId2,
5,
_cmsKitTestData.User2Id
));
await _ratingRepository.InsertAsync(new Rating(_guidGenerator.Create(),
_cmsKitTestData.EntityType2,
_cmsKitTestData.EntityId2,
@ -171,4 +178,4 @@ namespace Volo.CmsKit
));
}
}
}
}

@ -29,6 +29,7 @@
<abp-select asp-for="Role.ExtraProperties[propertyInfo.Name]"
label="@propertyInfo.GetLocalizedDisplayName(StringLocalizerFactory)"
autocomplete-api-url="@propertyInfo.Lookup.Url"
autocomplete-filter-param-name="@propertyInfo.Lookup.FilterParamName"
autocomplete-items-property-name="@propertyInfo.Lookup.ResultListPropertyName"
autocomplete-display-property-name="@propertyInfo.Lookup.DisplayPropertyName"
autocomplete-value-property-name="@propertyInfo.Lookup.ValuePropertyName"></abp-select>

@ -40,6 +40,7 @@
<abp-select asp-for="Role.ExtraProperties[propertyInfo.Name]"
label="@propertyInfo.GetLocalizedDisplayName(StringLocalizerFactory)"
autocomplete-api-url="@propertyInfo.Lookup.Url"
autocomplete-filter-param-name="@propertyInfo.Lookup.FilterParamName"
autocomplete-items-property-name="@propertyInfo.Lookup.ResultListPropertyName"
autocomplete-display-property-name="@propertyInfo.Lookup.DisplayPropertyName"
autocomplete-value-property-name="@propertyInfo.Lookup.ValuePropertyName"></abp-select>

@ -34,6 +34,7 @@
<abp-select asp-for="UserInfo.ExtraProperties[propertyInfo.Name]"
label="@propertyInfo.GetLocalizedDisplayName(StringLocalizerFactory)"
autocomplete-api-url="@propertyInfo.Lookup.Url"
autocomplete-filter-param-name="@propertyInfo.Lookup.FilterParamName"
autocomplete-items-property-name="@propertyInfo.Lookup.ResultListPropertyName"
autocomplete-display-property-name="@propertyInfo.Lookup.DisplayPropertyName"
autocomplete-value-property-name="@propertyInfo.Lookup.ValuePropertyName"></abp-select>

@ -36,6 +36,7 @@
<abp-select asp-for="UserInfo.ExtraProperties[propertyInfo.Name]"
label="@propertyInfo.GetLocalizedDisplayName(StringLocalizerFactory)"
autocomplete-api-url="@propertyInfo.Lookup.Url"
autocomplete-filter-param-name="@propertyInfo.Lookup.FilterParamName"
autocomplete-items-property-name="@propertyInfo.Lookup.ResultListPropertyName"
autocomplete-display-property-name="@propertyInfo.Lookup.DisplayPropertyName"
autocomplete-value-property-name="@propertyInfo.Lookup.ValuePropertyName"></abp-select>

@ -164,7 +164,7 @@
<Validations @ref="@ManageConnectionStringValidations" Model="@TenantInfo" ValidateOnLoad="false">
<Validation MessageLocalizer="@LH.Localize">
<Field>
<FieldLabel>@L["DisplayName:DefaultConnectionString"]</FieldLabel>
<FieldLabel>@L["DisplayName:UseSharedDatabase"]</FieldLabel>
<Check TValue="bool" @bind-Checked="@TenantInfo.UseSharedDatabase" Cursor="@Cursor.Pointer" />
</Field>
</Validation>
@ -191,4 +191,4 @@
@if (HasManageFeaturesPermission)
{
<FeatureManagementModal @ref="FeatureManagementModal" />
}
}

@ -29,6 +29,7 @@
<abp-select asp-for="Tenant.ExtraProperties[propertyInfo.Name]"
label="@propertyInfo.GetLocalizedDisplayName(StringLocalizerFactory)"
autocomplete-api-url="@propertyInfo.Lookup.Url"
autocomplete-filter-param-name="@propertyInfo.Lookup.FilterParamName"
autocomplete-items-property-name="@propertyInfo.Lookup.ResultListPropertyName"
autocomplete-display-property-name="@propertyInfo.Lookup.DisplayPropertyName"
autocomplete-value-property-name="@propertyInfo.Lookup.ValuePropertyName"></abp-select>

@ -25,6 +25,7 @@
<abp-select asp-for="Tenant.ExtraProperties[propertyInfo.Name]"
label="@propertyInfo.GetLocalizedDisplayName(StringLocalizerFactory)"
autocomplete-api-url="@propertyInfo.Lookup.Url"
autocomplete-filter-param-name="@propertyInfo.Lookup.FilterParamName"
autocomplete-items-property-name="@propertyInfo.Lookup.ResultListPropertyName"
autocomplete-display-property-name="@propertyInfo.Lookup.DisplayPropertyName"
autocomplete-value-property-name="@propertyInfo.Lookup.ValuePropertyName"></abp-select>

@ -87,7 +87,6 @@ The following is the list of supported scopes:
- **core**
- **theme-shared**
- **theme-basic**
- **account**
- **identity**
- **tenant-management**
- **feature-management**

@ -148,43 +148,6 @@
}
}
},
"account": {
"projectType": "library",
"root": "packages/account",
"sourceRoot": "packages/account/src",
"prefix": "abp",
"architect": {
"build": {
"builder": "@angular-devkit/build-ng-packagr:build",
"options": {
"tsConfig": "packages/account/tsconfig.lib.json",
"project": "packages/account/ng-package.json"
},
"configurations": {
"production": {
"tsConfig": "packages/account/tsconfig.lib.prod.json"
}
}
},
"test": {
"builder": "@angular-builders/jest:run",
"options": {
"coverage": true,
"passWithNoTests": true
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"packages/account/tsconfig.lib.json",
"packages/account/tsconfig.spec.json"
],
"exclude": ["**/node_modules/**"]
}
}
}
},
"permission-management": {
"projectType": "library",
"root": "packages/permission-management",

@ -7,11 +7,6 @@ const routes: Routes = [
pathMatch: 'full',
loadChildren: () => import('./home/home.module').then(m => m.HomeModule),
},
{
path: 'account',
loadChildren: () =>
import('@abp/ng.account').then(m => m.AccountModule.forLazy({ redirectUrl: '/' })),
},
{
path: 'identity',
loadChildren: () => import('@abp/ng.identity').then(m => m.IdentityModule.forLazy()),

@ -1,4 +1,3 @@
import { AccountConfigModule } from '@abp/ng.account/config';
import { CoreModule } from '@abp/ng.core';
import { registerLocale } from '@abp/ng.core/locale';
import { IdentityConfigModule } from '@abp/ng.identity/config';
@ -34,7 +33,6 @@ const INSPECTION_TOOLS = [
skipGetAppConfiguration: false,
}),
ThemeSharedModule.forRoot(),
AccountConfigModule.forRoot(),
IdentityConfigModule.forRoot(),
TenantManagementConfigModule.forRoot(),
SettingManagementConfigModule.forRoot(),

@ -26,7 +26,6 @@
"postinstall": "npm run compile:ivy"
},
"devDependencies": {
"@abp/ng.account": "~3.3.1",
"@abp/ng.core": "~3.3.1",
"@abp/ng.feature-management": "~3.3.1",
"@abp/ng.identity": "~3.3.1",

@ -1,3 +0,0 @@
<h1> @abp/ng.account </h1>
[docs.abp.io](https://docs.abp.io)

@ -1,7 +0,0 @@
{
"$schema": "../../../node_modules/ng-packagr/ng-package.schema.json",
"dest": "../../dist/account/config",
"lib": {
"entryFile": "src/public-api.ts"
}
}

@ -1,12 +0,0 @@
import { ModuleWithProviders, NgModule } from '@angular/core';
import { ACCOUNT_ROUTE_PROVIDERS } from './providers/route.provider';
@NgModule()
export class AccountConfigModule {
static forRoot(): ModuleWithProviders<AccountConfigModule> {
return {
ngModule: AccountConfigModule,
providers: [ACCOUNT_ROUTE_PROVIDERS],
};
}
}

@ -1,6 +0,0 @@
export const enum eAccountRouteNames {
Account = 'AbpAccount::Menu:Account',
Login = 'AbpAccount::Login',
Register = 'AbpAccount::Register',
ManageProfile = 'AbpAccount::ManageYourProfile',
}

@ -1,39 +0,0 @@
import { eLayoutType, RoutesService } from '@abp/ng.core';
import { APP_INITIALIZER } from '@angular/core';
import { eAccountRouteNames } from '../enums/route-names';
export const ACCOUNT_ROUTE_PROVIDERS = [
{ provide: APP_INITIALIZER, useFactory: configureRoutes, deps: [RoutesService], multi: true },
];
export function configureRoutes(routesService: RoutesService) {
return () => {
routesService.add([
{
path: '/account',
name: eAccountRouteNames.Account,
invisible: true,
layout: eLayoutType.application,
order: 1,
},
{
path: '/account/login',
name: eAccountRouteNames.Login,
parentName: eAccountRouteNames.Account,
order: 1,
},
{
path: '/account/register',
name: eAccountRouteNames.Register,
parentName: eAccountRouteNames.Account,
order: 2,
},
{
path: '/account/manage-profile',
name: eAccountRouteNames.ManageProfile,
parentName: eAccountRouteNames.Account,
order: 3,
},
]);
};
}

@ -1,3 +0,0 @@
export * from './account-config.module';
export * from './enums';
export * from './providers';

@ -1,6 +0,0 @@
const jestConfig = require('../../jest.config');
module.exports = {
...jestConfig,
name: 'account',
};

@ -1,8 +0,0 @@
{
"$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
"dest": "../../dist/account",
"lib": {
"entryFile": "src/public-api.ts"
},
"whitelistedNonPeerDependencies": ["@abp/ng.theme.shared"]
}

@ -1,7 +0,0 @@
module.exports = {
entryPoints: {
'.': {},
'./config': {},
'./dist': { ignore: true },
},
};

@ -1,16 +0,0 @@
{
"name": "@abp/ng.account",
"version": "3.3.1",
"homepage": "https://abp.io",
"repository": {
"type": "git",
"url": "https://github.com/abpframework/abp.git"
},
"dependencies": {
"@abp/ng.theme.shared": "~3.3.1",
"tslib": "^2.0.0"
},
"publishConfig": {
"access": "public"
}
}

@ -1,63 +0,0 @@
import {
DynamicLayoutComponent,
AuthGuard,
ReplaceableComponents,
ReplaceableRouteContainerComponent,
} from '@abp/ng.core';
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { LoginComponent } from './components/login/login.component';
import { ManageProfileComponent } from './components/manage-profile/manage-profile.component';
import { RegisterComponent } from './components/register/register.component';
import { eAccountComponents } from './enums/components';
import { AuthenticationFlowGuard } from './guards/authentication-flow.guard';
import { ManageProfileGuard } from './guards/manage-profile.guard';
const routes: Routes = [
{ path: '', pathMatch: 'full', redirectTo: 'login' },
{
path: '',
component: DynamicLayoutComponent,
children: [
{
path: 'login',
component: ReplaceableRouteContainerComponent,
canActivate: [AuthenticationFlowGuard],
data: {
replaceableComponent: {
key: eAccountComponents.Login,
defaultComponent: LoginComponent,
} as ReplaceableComponents.RouteData<LoginComponent>,
},
},
{
path: 'register',
component: ReplaceableRouteContainerComponent,
canActivate: [AuthenticationFlowGuard],
data: {
replaceableComponent: {
key: eAccountComponents.Register,
defaultComponent: RegisterComponent,
} as ReplaceableComponents.RouteData<RegisterComponent>,
},
},
{
path: 'manage-profile',
component: ReplaceableRouteContainerComponent,
canActivate: [AuthGuard, ManageProfileGuard],
data: {
replaceableComponent: {
key: eAccountComponents.ManageProfile,
defaultComponent: ManageProfileComponent,
} as ReplaceableComponents.RouteData<ManageProfileComponent>,
},
},
],
},
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
})
export class AccountRoutingModule {}

@ -1,59 +0,0 @@
import { CoreModule, LazyModuleFactory } from '@abp/ng.core';
import { ThemeSharedModule } from '@abp/ng.theme.shared';
import { ModuleWithProviders, NgModule, NgModuleFactory } from '@angular/core';
import { NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap';
import { NgxValidateCoreModule } from '@ngx-validate/core';
import { AccountRoutingModule } from './account-routing.module';
import { AuthWrapperComponent } from './components/auth-wrapper/auth-wrapper.component';
import { ChangePasswordComponent } from './components/change-password/change-password.component';
import { LoginComponent } from './components/login/login.component';
import { ManageProfileComponent } from './components/manage-profile/manage-profile.component';
import { PersonalSettingsComponent } from './components/personal-settings/personal-settings.component';
import { RegisterComponent } from './components/register/register.component';
import { TenantBoxComponent } from './components/tenant-box/tenant-box.component';
import { Options } from './models/options';
import { ACCOUNT_OPTIONS } from './tokens/options.token';
import { accountOptionsFactory } from './utils/factory-utils';
import { AuthenticationFlowGuard } from './guards/authentication-flow.guard';
import { ManageProfileGuard } from './guards/manage-profile.guard';
@NgModule({
declarations: [
AuthWrapperComponent,
LoginComponent,
RegisterComponent,
TenantBoxComponent,
ChangePasswordComponent,
ManageProfileComponent,
PersonalSettingsComponent,
],
imports: [
CoreModule,
AccountRoutingModule,
ThemeSharedModule,
NgbDropdownModule,
NgxValidateCoreModule,
],
exports: [],
})
export class AccountModule {
static forChild(options: Options): ModuleWithProviders<AccountModule> {
return {
ngModule: AccountModule,
providers: [
AuthenticationFlowGuard,
ManageProfileGuard,
{ provide: ACCOUNT_OPTIONS, useValue: options },
{
provide: 'ACCOUNT_OPTIONS',
useFactory: accountOptionsFactory,
deps: [ACCOUNT_OPTIONS],
},
],
};
}
static forLazy(options: Options): NgModuleFactory<AccountModule> {
return new LazyModuleFactory(AccountModule.forChild(options));
}
}

@ -1,26 +0,0 @@
<div class="row">
<div class="mx-auto col col-md-5">
<ng-container *ngIf="(isMultiTenancyEnabled$ | async) && multiTenancy.isTenantBoxVisible">
<abp-tenant-box *abpReplaceableTemplate="{ componentKey: tenantBoxKey }"></abp-tenant-box>
</ng-container>
<div class="abp-account-container">
<div
*ngIf="enableLocalLogin; else disableLocalLoginTemplate"
class="card mt-3 shadow-sm rounded"
>
<div class="card-body p-5">
<ng-content *ngTemplateOutlet="mainContentRef"></ng-content>
</div>
<ng-content *ngTemplateOutlet="cancelContentRef"></ng-content>
</div>
</div>
</div>
</div>
<ng-template #disableLocalLoginTemplate>
<div class="alert alert-warning">
<strong>{{ 'AbpAccount::InvalidLoginRequest' | abpLocalization }}</strong>
{{ 'AbpAccount::ThereAreNoLoginSchemesConfiguredForThisClient' | abpLocalization }}
</div>
</ng-template>

@ -1,45 +0,0 @@
import { ConfigState, SubscriptionService, MultiTenancyService } from '@abp/ng.core';
import { Component, Input, OnInit, TemplateRef } from '@angular/core';
import { Select, Store } from '@ngxs/store';
import { Observable } from 'rxjs';
import { eAccountComponents } from '../../enums/components';
import { Account } from '../../models/account';
@Component({
selector: 'abp-auth-wrapper',
templateUrl: './auth-wrapper.component.html',
exportAs: 'abpAuthWrapper',
providers: [SubscriptionService],
})
export class AuthWrapperComponent
implements Account.AuthWrapperComponentInputs, Account.AuthWrapperComponentOutputs, OnInit {
@Input()
readonly mainContentRef: TemplateRef<any>;
@Input()
readonly cancelContentRef: TemplateRef<any>;
@Select(ConfigState.getDeep('multiTenancy.isEnabled'))
isMultiTenancyEnabled$: Observable<boolean>;
enableLocalLogin = true;
tenantBoxKey = eAccountComponents.TenantBox;
constructor(
public readonly multiTenancy: MultiTenancyService,
private store: Store,
private subscription: SubscriptionService,
) {}
ngOnInit() {
this.subscription.addOne(
this.store.select(ConfigState.getSetting('Abp.Account.EnableLocalLogin')),
value => {
if (value) {
this.enableLocalLogin = value.toLowerCase() !== 'false';
}
},
);
}
}

@ -1,48 +0,0 @@
<form [formGroup]="form" (ngSubmit)="onSubmit()" [mapErrorsFn]="mapErrorsFn" validateOnSubmit>
<div *ngIf="!hideCurrentPassword" class="form-group">
<label for="current-password">{{
'AbpIdentity::DisplayName:CurrentPassword' | abpLocalization
}}</label
><span> * </span
><input
type="password"
id="current-password"
class="form-control"
formControlName="password"
autofocus
autocomplete="current-password"
/>
</div>
<div class="form-group">
<label for="new-password">{{ 'AbpIdentity::DisplayName:NewPassword' | abpLocalization }}</label
><span> * </span
><input
type="password"
id="new-password"
class="form-control"
formControlName="newPassword"
autocomplete="new-password"
/>
</div>
<div class="form-group">
<label for="confirm-new-password">{{
'AbpIdentity::DisplayName:NewPasswordConfirm' | abpLocalization
}}</label
><span> * </span
><input
type="password"
id="confirm-new-password"
class="form-control"
formControlName="repeatNewPassword"
autocomplete="new-password"
/>
</div>
<abp-button
iconClass="fa fa-check"
buttonClass="btn btn-primary color-white"
buttonType="submit"
[loading]="inProgress"
[disabled]="form?.invalid"
>{{ 'AbpIdentity::Save' | abpLocalization }}</abp-button
>
</form>

@ -1,100 +0,0 @@
import { ChangePassword, ProfileState } from '@abp/ng.core';
import { getPasswordValidators, ToasterService } from '@abp/ng.theme.shared';
import { Component, Injector, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators, FormControl } from '@angular/forms';
import { comparePasswords, Validation } from '@ngx-validate/core';
import { Store } from '@ngxs/store';
import { finalize } from 'rxjs/operators';
import snq from 'snq';
import { Account } from '../../models/account';
const { required } = Validators;
const PASSWORD_FIELDS = ['newPassword', 'repeatNewPassword'];
@Component({
selector: 'abp-change-password-form',
templateUrl: './change-password.component.html',
exportAs: 'abpChangePasswordForm',
})
export class ChangePasswordComponent
implements OnInit, Account.ChangePasswordComponentInputs, Account.ChangePasswordComponentOutputs {
form: FormGroup;
inProgress: boolean;
hideCurrentPassword: boolean;
mapErrorsFn: Validation.MapErrorsFn = (errors, groupErrors, control) => {
if (PASSWORD_FIELDS.indexOf(String(control.name)) < 0) return errors;
return errors.concat(groupErrors.filter(({ key }) => key === 'passwordMismatch'));
};
constructor(
private injector: Injector,
private fb: FormBuilder,
private store: Store,
private toasterService: ToasterService,
) {}
ngOnInit(): void {
this.hideCurrentPassword = !this.store.selectSnapshot(ProfileState.getProfile).hasPassword;
const passwordValidations = getPasswordValidators(this.injector);
this.form = this.fb.group(
{
password: ['', required],
newPassword: [
'',
{
validators: [required, ...passwordValidations],
},
],
repeatNewPassword: [
'',
{
validators: [required, ...passwordValidations],
},
],
},
{
validators: [comparePasswords(PASSWORD_FIELDS)],
},
);
if (this.hideCurrentPassword) this.form.removeControl('password');
}
onSubmit() {
if (this.form.invalid) return;
this.inProgress = true;
this.store
.dispatch(
new ChangePassword({
...(!this.hideCurrentPassword && { currentPassword: this.form.get('password').value }),
newPassword: this.form.get('newPassword').value,
}),
)
.pipe(finalize(() => (this.inProgress = false)))
.subscribe({
next: () => {
this.form.reset();
this.toasterService.success('AbpAccount::PasswordChangedMessage', '', {
life: 5000,
});
if (this.hideCurrentPassword) {
this.hideCurrentPassword = false;
this.form.addControl('password', new FormControl('', [required]));
}
},
error: err => {
this.toasterService.error(
snq(() => err.error.error.message, 'AbpAccount::DefaultErrorMessage'),
);
},
});
}
}

@ -1,6 +0,0 @@
export * from './change-password/change-password.component';
export * from './login/login.component';
export * from './manage-profile/manage-profile.component';
export * from './register/register.component';
export * from './personal-settings/personal-settings.component';
export * from './tenant-box/tenant-box.component';

@ -1,74 +0,0 @@
<abp-auth-wrapper
*abpReplaceableTemplate="{
componentKey: authWrapperKey,
inputs: {
mainContentRef: { value: mainContentRef },
cancelContentRef: { value: cancelContentRef }
}
}"
[mainContentRef]="mainContentRef"
[cancelContentRef]="cancelContentRef"
>
</abp-auth-wrapper>
<ng-template #mainContentRef>
<h4>{{ 'AbpAccount::Login' | abpLocalization }}</h4>
<strong *ngIf="isSelfRegistrationEnabled">
{{ 'AbpAccount::AreYouANewUser' | abpLocalization }}
<a class="text-decoration-none" routerLink="/account/register">{{
'AbpAccount::Register' | abpLocalization
}}</a>
</strong>
<form [formGroup]="form" (ngSubmit)="onSubmit()" validateOnSubmit class="mt-4">
<div class="form-group">
<label for="login-input-user-name-or-email-address">{{
'AbpAccount::UserNameOrEmailAddress' | abpLocalization
}}</label>
<input
class="form-control"
type="text"
id="login-input-user-name-or-email-address"
formControlName="username"
autocomplete="username"
autofocus
/>
</div>
<div class="form-group">
<label for="login-input-password">{{ 'AbpAccount::Password' | abpLocalization }}</label>
<input
class="form-control"
type="password"
id="login-input-password"
formControlName="password"
autocomplete="current-password"
/>
</div>
<div class="form-check" validationTarget validationStyle>
<label class="form-check-label" for="login-input-remember-me">
<input
class="form-check-input"
type="checkbox"
id="login-input-remember-me"
formControlName="remember"
/>
{{ 'AbpAccount::RememberMe' | abpLocalization }}
</label>
</div>
<abp-button
[loading]="inProgress"
buttonType="submit"
name="Action"
buttonClass="btn-block btn-lg mt-3 btn btn-primary"
>
{{ 'AbpAccount::Login' | abpLocalization }}
</abp-button>
</form>
</ng-template>
<ng-template #cancelContentRef>
<div class="card-footer text-center border-0">
<a routerLink="/">
<button type="button" name="Action" value="Cancel" class="px-2 py-0 btn btn-link">
{{ 'AbpAccount::Cancel' | abpLocalization }}
</button>
</a>
</div>
</ng-template>

@ -1,70 +0,0 @@
import { AuthService, ConfigState } from '@abp/ng.core';
import { ToasterService } from '@abp/ng.theme.shared';
import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Store } from '@ngxs/store';
import { OAuthService } from 'angular-oauth2-oidc';
import { throwError } from 'rxjs';
import { catchError, finalize } from 'rxjs/operators';
import snq from 'snq';
import { eAccountComponents } from '../../enums/components';
const { maxLength, minLength, required } = Validators;
@Component({
selector: 'abp-login',
templateUrl: './login.component.html',
})
export class LoginComponent implements OnInit {
form: FormGroup;
inProgress: boolean;
isSelfRegistrationEnabled = true;
authWrapperKey = eAccountComponents.AuthWrapper;
constructor(
private fb: FormBuilder,
private oauthService: OAuthService,
private store: Store,
private toasterService: ToasterService,
private authService: AuthService,
) {}
ngOnInit() {
this.isSelfRegistrationEnabled =
(
(this.store.selectSnapshot(
ConfigState.getSetting('Abp.Account.IsSelfRegistrationEnabled'),
) as string) || ''
).toLowerCase() !== 'false';
this.form = this.fb.group({
username: ['', [required, maxLength(255)]],
password: ['', [required, maxLength(128)]],
remember: [false],
});
}
onSubmit() {
if (this.form.invalid) return;
this.inProgress = true;
this.authService
.login(this.form.get('username').value, this.form.get('password').value)
.pipe(
catchError(err => {
this.toasterService.error(
snq(() => err.error.error_description) ||
snq(() => err.error.error.message, 'AbpAccount::DefaultErrorMessage'),
'AbpAccount::Error',
{ life: 7000 },
);
return throwError(err);
}),
finalize(() => (this.inProgress = false)),
)
.subscribe(() => {});
}
}

@ -1,58 +0,0 @@
<div id="AbpContentToolbar"></div>
<div class="card border-0 shadow-sm min-h-400" [abpLoading]="!isProfileLoaded">
<div class="card-body">
<div class="row">
<div class="col-12 col-md-3">
<ul class="nav flex-column nav-pills" id="nav-tab" role="tablist">
<li
*ngIf="!hideChangePasswordTab && isProfileLoaded"
class="nav-item"
(click)="selectedTab = 0"
>
<a
class="nav-link"
[ngClass]="{ active: selectedTab === 0 }"
role="tab"
href="javascript:void(0)"
>{{ 'AbpUi::ChangePassword' | abpLocalization }}</a
>
</li>
<li class="nav-item mb-2" (click)="selectedTab = 1">
<a
class="nav-link"
[ngClass]="{ active: selectedTab === 1 }"
role="tab"
href="javascript:void(0)"
>{{ 'AbpAccount::PersonalSettings' | abpLocalization }}</a
>
</li>
</ul>
</div>
<div *ngIf="isProfileLoaded" class="col-12 col-md-9">
<div class="tab-content" *ngIf="selectedTab === 0" [@fadeIn]>
<div class="tab-pane active" role="tabpanel">
<h4>
{{ 'AbpIdentity::ChangePassword' | abpLocalization }}
<hr />
</h4>
<abp-change-password-form
*abpReplaceableTemplate="{ componentKey: changePasswordKey }"
></abp-change-password-form>
</div>
</div>
<div class="tab-content" *ngIf="selectedTab === 1" [@fadeIn]>
<div class="tab-pane active" role="tabpanel">
<h4>
{{ 'AbpIdentity::PersonalSettings' | abpLocalization }}
<hr />
</h4>
<abp-personal-settings-form
*abpReplaceableTemplate="{ componentKey: personalSettingsKey }"
></abp-personal-settings-form>
</div>
</div>
</div>
</div>
</div>
</div>

@ -1,42 +0,0 @@
import { fadeIn } from '@abp/ng.theme.shared';
import { transition, trigger, useAnimation } from '@angular/animations';
import { Component, OnInit } from '@angular/core';
import { eAccountComponents } from '../../enums/components';
import { Store } from '@ngxs/store';
import { GetProfile, ProfileState } from '@abp/ng.core';
@Component({
selector: 'abp-manage-profile',
templateUrl: './manage-profile.component.html',
animations: [trigger('fadeIn', [transition(':enter', useAnimation(fadeIn))])],
styles: [
`
.min-h-400 {
min-height: 400px;
}
`,
],
})
export class ManageProfileComponent implements OnInit {
selectedTab = 0;
changePasswordKey = eAccountComponents.ChangePassword;
personalSettingsKey = eAccountComponents.PersonalSettings;
isProfileLoaded: boolean;
hideChangePasswordTab: boolean;
constructor(private store: Store) {}
ngOnInit() {
this.store.dispatch(new GetProfile()).subscribe(() => {
this.isProfileLoaded = true;
if (this.store.selectSnapshot(ProfileState.getProfile).isExternal) {
this.hideChangePasswordTab = true;
this.selectedTab = 1;
}
});
}
}

@ -1,46 +0,0 @@
<form validateOnSubmit *ngIf="form" [formGroup]="form" (ngSubmit)="submit()">
<div class="form-group">
<label for="username">{{ 'AbpIdentity::DisplayName:UserName' | abpLocalization }}</label
><span> * </span
><input
type="text"
id="username"
class="form-control"
formControlName="userName"
autofocus
(keydown.space)="$event.preventDefault()"
/>
</div>
<div class="row">
<div class="col col-md-6">
<div class="form-group">
<label for="name">{{ 'AbpIdentity::DisplayName:Name' | abpLocalization }}</label
><input type="text" id="name" class="form-control" formControlName="name" />
</div>
</div>
<div class="col col-md-6">
<div class="form-group">
<label for="surname">{{ 'AbpIdentity::DisplayName:Surname' | abpLocalization }}</label
><input type="text" id="surname" class="form-control" formControlName="surname" />
</div>
</div>
</div>
<div class="form-group">
<label for="email-address">{{ 'AbpIdentity::DisplayName:Email' | abpLocalization }}</label
><span> * </span
><input type="text" id="email-address" class="form-control" formControlName="email" />
</div>
<div class="form-group">
<label for="phone-number">{{ 'AbpIdentity::DisplayName:PhoneNumber' | abpLocalization }}</label
><input type="text" id="phone-number" class="form-control" formControlName="phoneNumber" />
</div>
<abp-button
buttonType="submit"
iconClass="fa fa-check"
buttonClass="btn btn-primary color-white"
[loading]="inProgress"
[disabled]="form?.invalid"
>
{{ 'AbpIdentity::Save' | abpLocalization }}</abp-button
>
</form>

@ -1,57 +0,0 @@
import { ProfileState, UpdateProfile } from '@abp/ng.core';
import { ToasterService } from '@abp/ng.theme.shared';
import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Store } from '@ngxs/store';
import { finalize } from 'rxjs/operators';
import { Account } from '../../models/account';
const { maxLength, required, email } = Validators;
@Component({
selector: 'abp-personal-settings-form',
templateUrl: './personal-settings.component.html',
exportAs: 'abpPersonalSettingsForm',
})
export class PersonalSettingsComponent
implements
OnInit,
Account.PersonalSettingsComponentInputs,
Account.PersonalSettingsComponentOutputs {
form: FormGroup;
inProgress: boolean;
constructor(
private fb: FormBuilder,
private store: Store,
private toasterService: ToasterService,
) {}
ngOnInit() {
this.buildForm();
}
buildForm() {
const profile = this.store.selectSnapshot(ProfileState.getProfile);
this.form = this.fb.group({
userName: [profile.userName, [required, maxLength(256)]],
email: [profile.email, [required, email, maxLength(256)]],
name: [profile.name || '', [maxLength(64)]],
surname: [profile.surname || '', [maxLength(64)]],
phoneNumber: [profile.phoneNumber || '', [maxLength(16)]],
});
}
submit() {
if (this.form.invalid) return;
this.inProgress = true;
this.store
.dispatch(new UpdateProfile(this.form.value))
.pipe(finalize(() => (this.inProgress = false)))
.subscribe(() => {
this.toasterService.success('AbpAccount::PersonalSettingsSaved', 'Success', { life: 5000 });
});
}
}

@ -1,63 +0,0 @@
<abp-auth-wrapper
*abpReplaceableTemplate="{
componentKey: authWrapperKey,
inputs: {
mainContentRef: { value: mainContentRef }
}
}"
[mainContentRef]="mainContentRef"
>
</abp-auth-wrapper>
<ng-template #mainContentRef>
<h4>{{ 'AbpAccount::Register' | abpLocalization }}</h4>
<strong>
{{ 'AbpAccount::AlreadyRegistered' | abpLocalization }}
<a class="text-decoration-none" routerLink="/account/login">{{
'AbpAccount::Login' | abpLocalization
}}</a>
</strong>
<form
*ngIf="isSelfRegistrationEnabled"
[formGroup]="form"
(ngSubmit)="onSubmit()"
validateOnSubmit
class="mt-4"
>
<div class="form-group">
<label for="input-user-name">{{ 'AbpAccount::UserName' | abpLocalization }}</label
><span> * </span
><input
autofocus
type="text"
id="input-user-name"
class="form-control"
formControlName="username"
autocomplete="username"
/>
</div>
<div class="form-group">
<label for="input-email-address">{{ 'AbpAccount::EmailAddress' | abpLocalization }}</label
><span> * </span
><input type="email" id="input-email-address" class="form-control" formControlName="email" />
</div>
<div class="form-group">
<label for="input-password">{{ 'AbpAccount::Password' | abpLocalization }}</label
><span> * </span
><input
type="password"
id="input-password"
class="form-control"
formControlName="password"
autocomplete="current-password"
/>
</div>
<abp-button
[loading]="inProgress"
buttonType="submit"
name="Action"
buttonClass="btn-block btn-lg mt-3 btn btn-primary"
>
{{ 'AbpAccount::Register' | abpLocalization }}
</abp-button>
</form>
</ng-template>

@ -1,93 +0,0 @@
import { AuthService, ConfigState } from '@abp/ng.core';
import { getPasswordValidators, ToasterService } from '@abp/ng.theme.shared';
import { Component, Injector, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Store } from '@ngxs/store';
import { OAuthService } from 'angular-oauth2-oidc';
import { throwError } from 'rxjs';
import { catchError, finalize, switchMap } from 'rxjs/operators';
import snq from 'snq';
import { RegisterRequest } from '../../models';
import { AccountService } from '../../services/account.service';
import { eAccountComponents } from '../../enums/components';
const { maxLength, required, email } = Validators;
@Component({
selector: 'abp-register',
templateUrl: './register.component.html',
})
export class RegisterComponent implements OnInit {
form: FormGroup;
inProgress: boolean;
isSelfRegistrationEnabled = true;
authWrapperKey = eAccountComponents.AuthWrapper;
constructor(
private injector: Injector,
private fb: FormBuilder,
private accountService: AccountService,
private oauthService: OAuthService,
private store: Store,
private toasterService: ToasterService,
private authService: AuthService,
) {}
ngOnInit() {
this.isSelfRegistrationEnabled =
(
this.store.selectSnapshot(
ConfigState.getSetting('Abp.Account.IsSelfRegistrationEnabled'),
) || ''
).toLowerCase() !== 'false';
if (!this.isSelfRegistrationEnabled) {
this.toasterService.warn(
{
key: 'AbpAccount::SelfRegistrationDisabledMessage',
defaultValue: 'Self registration is disabled.',
},
null,
{ life: 10000 },
);
return;
}
this.form = this.fb.group({
username: ['', [required, maxLength(255)]],
password: ['', [required, ...getPasswordValidators(this.injector)]],
email: ['', [required, email]],
});
}
onSubmit() {
if (this.form.invalid) return;
this.inProgress = true;
const newUser = {
userName: this.form.get('username').value,
password: this.form.get('password').value,
emailAddress: this.form.get('email').value,
appName: 'Angular',
} as RegisterRequest;
this.accountService
.register(newUser)
.pipe(
switchMap(() => this.authService.login(newUser.userName, newUser.password)),
catchError(err => {
this.toasterService.error(
snq(() => err.error.error_description) ||
snq(() => err.error.error.message, 'AbpAccount::DefaultErrorMessage'),
'Error',
{ life: 7000 },
);
return throwError(err);
}),
finalize(() => (this.inProgress = false)),
)
.subscribe();
}
}

@ -1,63 +0,0 @@
<ng-container *ngIf="(currentTenant$ | async) || {} as currentTenant">
<div class="card shadow-sm rounded mb-3">
<div class="card-body px-5">
<div class="row">
<div class="col">
<span style="font-size: 0.8em;" class="text-uppercase text-muted">{{
'AbpUiMultiTenancy::Tenant' | abpLocalization
}}</span
><br />
<h6 class="m-0 d-inline-block">
<i>{{ currentTenant.name || ('AbpUiMultiTenancy::NotSelected' | abpLocalization) }}</i>
</h6>
</div>
<div class="col-auto">
<a
id="AbpTenantSwitchLink"
href="javascript:void(0);"
class="btn btn-sm mt-3 btn-outline-primary"
(click)="onSwitch()"
>{{ 'AbpUiMultiTenancy::Switch' | abpLocalization }}</a
>
</div>
</div>
</div>
</div>
<abp-modal size="md" [(visible)]="isModalVisible" [busy]="modalBusy">
<ng-template #abpHeader>
<h5>Switch Tenant</h5>
</ng-template>
<ng-template #abpBody>
<form (ngSubmit)="save()">
<div class="mt-2">
<div class="form-group">
<label for="name">{{ 'AbpUiMultiTenancy::Name' | abpLocalization }}</label>
<input
[(ngModel)]="name"
type="text"
id="name"
name="tenant"
class="form-control"
autofocus
/>
</div>
<p>{{ 'AbpUiMultiTenancy::SwitchTenantHint' | abpLocalization }}</p>
</div>
</form>
</ng-template>
<ng-template #abpFooter>
<button #abpClose type="button" class="btn btn-secondary">
{{ 'AbpTenantManagement::Cancel' | abpLocalization }}
</button>
<abp-button
type="abp-button"
iconClass="fa fa-check"
(click)="save()"
[disabled]="currentTenant?.name === name"
>
<span>{{ 'AbpTenantManagement::Save' | abpLocalization }}</span>
</abp-button>
</ng-template>
</abp-modal>
</ng-container>

@ -1,69 +0,0 @@
import { ABP, GetAppConfiguration, SessionStateService } from '@abp/ng.core';
import { ToasterService } from '@abp/ng.theme.shared';
import { Component } from '@angular/core';
import { Select, Store } from '@ngxs/store';
import { Observable } from 'rxjs';
import { finalize, take } from 'rxjs/operators';
import { Account } from '../../models/account';
import { AccountService } from '../../services/account.service';
@Component({
selector: 'abp-tenant-box',
templateUrl: './tenant-box.component.html',
})
export class TenantBoxComponent
implements Account.TenantBoxComponentInputs, Account.TenantBoxComponentOutputs {
currentTenant$ = this.sessionState.getTenant$();
name: string;
isModalVisible: boolean;
modalBusy: boolean;
constructor(
private store: Store,
private toasterService: ToasterService,
private accountService: AccountService,
private sessionState: SessionStateService,
) {}
onSwitch() {
const tenant = this.sessionState.getTenant;
this.name = tenant?.name;
this.isModalVisible = true;
}
save() {
if (!this.name) {
this.setTenant(null);
this.isModalVisible = false;
return;
}
this.modalBusy = true;
this.accountService
.findTenant(this.name)
.pipe(finalize(() => (this.modalBusy = false)))
.subscribe(({ success, tenantId: id, name }) => {
if (!success) {
this.showError();
return;
}
this.setTenant({ id, name });
this.isModalVisible = false;
});
}
private setTenant(tenant: ABP.BasicItem) {
this.sessionState.setTenant(tenant);
return this.store.dispatch(new GetAppConfiguration());
}
private showError() {
this.toasterService.error('AbpUiMultiTenancy::GivenTenantIsNotAvailable', 'AbpUi::Error', {
messageLocalizationParams: [this.name],
});
}
}

@ -1,9 +0,0 @@
export const enum eAccountComponents {
Login = 'Account.LoginComponent',
Register = 'Account.RegisterComponent',
ManageProfile = 'Account.ManageProfileComponent',
TenantBox = 'Account.TenantBoxComponent',
AuthWrapper = 'Account.AuthWrapperComponent',
ChangePassword = 'Account.ChangePasswordComponent',
PersonalSettings = 'Account.PersonalSettingsComponent',
}

@ -1,15 +0,0 @@
import { AuthService } from '@abp/ng.core';
import { Injectable } from '@angular/core';
import { CanActivate } from '@angular/router';
@Injectable()
export class AuthenticationFlowGuard implements CanActivate {
constructor(private authService: AuthService) {}
canActivate() {
if (this.authService.isInternalAuth) return true;
this.authService.initLogin();
return false;
}
}

@ -1,2 +0,0 @@
export * from './authentication-flow.guard';
export * from './manage-profile.guard';

@ -1,18 +0,0 @@
import { EnvironmentService } from '@abp/ng.core';
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot } from '@angular/router';
@Injectable()
export class ManageProfileGuard implements CanActivate {
constructor(private environment: EnvironmentService) {}
canActivate(_: ActivatedRouteSnapshot, __: RouterStateSnapshot) {
const env = this.environment.getEnvironment();
if (env.oAuthConfig.responseType === 'code') {
window.location.href = `${env.oAuthConfig.issuer}/Account/Manage?returnUrl=${window.location.href}`;
return false;
} else {
return true;
}
}
}

@ -1,18 +0,0 @@
import { TemplateRef } from '@angular/core';
export namespace Account {
export interface AuthWrapperComponentInputs {
readonly mainContentRef: TemplateRef<any>;
readonly cancelContentRef?: TemplateRef<any>;
}
//tslint:disable
export interface AuthWrapperComponentOutputs {}
export interface TenantBoxComponentInputs {}
export interface TenantBoxComponentOutputs {}
export interface PersonalSettingsComponentInputs {}
export interface PersonalSettingsComponentOutputs {}
export interface ChangePasswordComponentInputs {}
export interface ChangePasswordComponentOutputs {}
// tslint:enable
}

@ -1,4 +0,0 @@
export * from './account';
export * from './options';
export * from './user';
export * from './tenant';

@ -1,3 +0,0 @@
export interface Options {
redirectUrl?: string;
}

@ -1,5 +0,0 @@
export interface TenantIdResponse {
success: boolean;
tenantId: string;
name: string;
}

@ -1,28 +0,0 @@
export interface RegisterRequest {
userName: string;
emailAddress: string;
password: string;
appName?: string;
}
export interface RegisterResponse {
tenantId: string;
userName: string;
name: string;
surname: string;
email: string;
emailConfirmed: boolean;
phoneNumber: string;
phoneNumberConfirmed: boolean;
lockoutEnabled: boolean;
lockoutEnd: string;
concurrencyStamp: string;
isDeleted: boolean;
deleterId: string;
deletionTime: string;
lastModificationTime: string;
lastModifierId: string;
creationTime: string;
creatorId: string;
id: string;
}

@ -1,35 +0,0 @@
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { RestService, Rest } from '@abp/ng.core';
import { RegisterResponse, RegisterRequest, TenantIdResponse } from '../models';
@Injectable({
providedIn: 'root',
})
export class AccountService {
apiName = 'AbpAccount';
constructor(private rest: RestService) {}
findTenant(tenantName: string): Observable<TenantIdResponse> {
const request: Rest.Request<null> = {
method: 'GET',
url: `/api/abp/multi-tenancy/tenants/by-name/${tenantName}`,
};
return this.rest.request<null, TenantIdResponse>(request, { apiName: this.apiName });
}
register(body: RegisterRequest): Observable<RegisterResponse> {
const request: Rest.Request<RegisterRequest> = {
method: 'POST',
url: '/api/account/register',
body,
};
return this.rest.request<RegisterRequest, RegisterResponse>(request, {
skipHandleError: true,
apiName: this.apiName,
});
}
}

@ -1 +0,0 @@
export * from './account.service';

@ -1,38 +0,0 @@
import { createHttpFactory, HttpMethod, SpectatorHttp } from '@ngneat/spectator/jest';
import { AccountService } from '../services/account.service';
import { Store } from '@ngxs/store';
import { RestService, CORE_OPTIONS } from '@abp/ng.core';
import { RegisterRequest } from '../models/user';
describe('AccountService', () => {
let spectator: SpectatorHttp<AccountService>;
const createHttp = createHttpFactory({
dataService: AccountService,
providers: [RestService, { provide: CORE_OPTIONS, useValue: { environment: {} } }],
mocks: [Store],
});
beforeEach(() => (spectator = createHttp()));
it('should send a GET to find tenant', () => {
spectator.inject(Store).selectSnapshot.andReturn('https://abp.io');
spectator.service.findTenant('test').subscribe();
spectator.expectOne(
'https://abp.io/api/abp/multi-tenancy/tenants/by-name/test',
HttpMethod.GET,
);
});
it('should send a POST to register API', () => {
const mock = {
userName: 'test',
emailAddress: 'test@test.com',
password: 'test1234',
appName: 'Angular',
} as RegisterRequest;
spectator.inject(Store).selectSnapshot.andReturn('https://abp.io');
spectator.service.register(mock).subscribe();
const req = spectator.expectOne('https://abp.io/api/account/register', HttpMethod.POST);
expect(req.request.body).toEqual(mock);
});
});

@ -1 +0,0 @@
export * from './options.token';

@ -1,4 +0,0 @@
import { InjectionToken } from '@angular/core';
import { Options } from '../models/options';
export const ACCOUNT_OPTIONS = new InjectionToken<Options>('ACCOUNT_OPTIONS');

@ -1,8 +0,0 @@
import { Options } from '../models/options';
export function accountOptionsFactory(options: Options) {
return {
redirectUrl: '/',
...options,
};
}

@ -1 +0,0 @@
export * from './factory-utils';

@ -1,7 +0,0 @@
export * from './lib/account.module';
export * from './lib/components';
export * from './lib/enums';
export * from './lib/guards';
export * from './lib/models';
export * from './lib/services';
export * from './lib/tokens';

@ -1,19 +0,0 @@
{
"extends": "../../tsconfig.prod.json",
"compilerOptions": {
"outDir": "../../out-tsc/lib",
"target": "es2015",
"declaration": true,
"inlineSources": true,
"types": [],
"lib": ["dom", "es2018"]
},
"angularCompilerOptions": {
"skipTemplateCodegen": true,
"strictMetadataEmit": true,
"fullTemplateTypeCheck": true,
"strictInjectionParameters": true,
"enableResourceInlining": true
},
"exclude": ["src/test.ts", "**/*.spec.ts"]
}

@ -1,6 +0,0 @@
{
"extends": "./tsconfig.lib.json",
"angularCompilerOptions": {
"enableIvy": false
}
}

@ -1,11 +0,0 @@
{
"extends": "../../tsconfig.prod.json",
"compilerOptions": {
"emitDecoratorMetadata": true,
"esModuleInterop": true,
"outDir": "../../dist/out-tsc",
"module": "commonjs",
"types": ["jest", "node"]
},
"include": ["**/*.spec.ts", "**/*.d.ts"]
}

@ -1,17 +0,0 @@
{
"extends": "../../tslint.json",
"rules": {
"directive-selector": [
true,
"attribute",
"abp",
"camelCase"
],
"component-selector": [
true,
"element",
"abp",
"kebab-case"
]
}
}

@ -1,32 +1,22 @@
import { Injectable, Injector } from '@angular/core';
import {
ActivatedRouteSnapshot,
CanActivate,
Router,
RouterStateSnapshot,
UrlTree,
} from '@angular/router';
import { Injectable } from '@angular/core';
import { CanActivate, UrlTree } from '@angular/router';
import { OAuthService } from 'angular-oauth2-oidc';
import { Observable } from 'rxjs';
import { AuthService } from '../services/auth.service';
@Injectable({
providedIn: 'root',
})
export class AuthGuard implements CanActivate {
constructor(private oauthService: OAuthService, private injector: Injector) {}
canActivate(
_: ActivatedRouteSnapshot,
state: RouterStateSnapshot,
): Observable<boolean> | boolean | UrlTree {
const router = this.injector.get(Router);
constructor(private oauthService: OAuthService, private authService: AuthService) {}
canActivate(): Observable<boolean> | boolean | UrlTree {
const hasValidAccessToken = this.oauthService.hasValidAccessToken();
if (hasValidAccessToken) {
return hasValidAccessToken;
}
router.navigate(['/account/login'], { state: { redirectUrl: state.url } });
this.authService.initLogin();
return true;
}
}

@ -1,16 +1,7 @@
import { HttpHeaders } from '@angular/common/http';
import { Inject, Injectable, Injector, Optional } from '@angular/core';
import { Navigate } from '@ngxs/router-plugin';
import { Store } from '@ngxs/store';
import { OAuthService } from 'angular-oauth2-oidc';
import { from, Observable } from 'rxjs';
import { switchMap, take, tap } from 'rxjs/operators';
import snq from 'snq';
import { Injectable, Injector } from '@angular/core';
import { Observable } from 'rxjs';
import { AuthFlowStrategy, AUTH_FLOW_STRATEGY } from '../strategies/auth-flow.strategy';
import { ApplicationConfigurationService } from './application-configuration.service';
import { ConfigStateService } from './config-state.service';
import { EnvironmentService } from './environment.service';
import { SessionStateService } from './session-state.service';
@Injectable({
providedIn: 'root',
@ -23,16 +14,7 @@ export class AuthService {
return this.strategy.isInternalAuth;
}
constructor(
private environment: EnvironmentService,
private injector: Injector,
private oAuthService: OAuthService,
private store: Store,
private sessionState: SessionStateService,
private configState: ConfigStateService,
private appConfigService: ApplicationConfigurationService,
@Optional() @Inject('ACCOUNT_OPTIONS') private options: any,
) {
constructor(private environment: EnvironmentService, private injector: Injector) {
this.setStrategy();
this.listenToSetEnvironment();
}
@ -44,38 +26,13 @@ export class AuthService {
if (this.strategy) this.strategy.destroy();
this.flow = flow;
this.strategy =
this.flow === 'code'
? AUTH_FLOW_STRATEGY.Code(this.injector)
: AUTH_FLOW_STRATEGY.Password(this.injector);
this.strategy = AUTH_FLOW_STRATEGY.Code(this.injector);
};
private listenToSetEnvironment() {
this.environment.createOnUpdateStream(state => state.oAuthConfig).subscribe(this.setStrategy);
}
login(username: string, password: string): Observable<any> {
const tenant = this.sessionState.getTenant();
return from(
this.oAuthService.fetchTokenUsingPasswordFlow(
username,
password,
new HttpHeaders({ ...(tenant && tenant.id && { __tenant: tenant.id }) }),
),
).pipe(
switchMap(() =>
this.appConfigService.getConfiguration().pipe(tap(res => this.configState.setState(res))),
),
tap(() => {
const redirectUrl =
snq(() => window.history.state.redirectUrl) || (this.options || {}).redirectUrl || '/';
this.store.dispatch(new Navigate([redirectUrl]));
}),
take(1),
);
}
async init() {
return await this.strategy.init();
}

@ -1,14 +1,11 @@
import { Injector } from '@angular/core';
import { Router } from '@angular/router';
import { Store } from '@ngxs/store';
import { AuthConfig, OAuthService, OAuthStorage } from 'angular-oauth2-oidc';
import { Observable, of } from 'rxjs';
import { switchMap, tap } from 'rxjs/operators';
import { RestOccurError } from '../actions/rest.actions';
import { ApplicationConfigurationService } from '../services/application-configuration.service';
import { ConfigStateService } from '../services/config-state.service';
import { EnvironmentService } from '../services/environment.service';
import { RestService } from '../services/rest.service';
export const oAuthStorage = localStorage;
@ -83,49 +80,10 @@ export class AuthCodeFlowStrategy extends AuthFlowStrategy {
destroy() {}
}
export class AuthPasswordFlowStrategy extends AuthFlowStrategy {
readonly isInternalAuth = true;
login() {
const router = this.injector.get(Router);
router.navigateByUrl('/account/login');
}
checkIfInternalAuth() {
return true;
}
logout() {
const rest = this.injector.get(RestService);
const issuer = this.environment.getEnvironment().oAuthConfig.issuer;
return rest
.request(
{
method: 'GET',
url: '/api/account/logout',
},
null,
issuer,
)
.pipe(
tap(() => this.oAuthService.logOut()),
switchMap(() =>
this.appConfigService.getConfiguration().pipe(tap(res => this.configState.setState(res))),
),
);
}
destroy() {}
}
export const AUTH_FLOW_STRATEGY = {
Code(injector: Injector) {
return new AuthCodeFlowStrategy(injector);
},
Password(injector: Injector) {
return new AuthPasswordFlowStrategy(injector);
},
};
export function clearOAuthStorage(storage: OAuthStorage = oAuthStorage) {

@ -1,5 +1,10 @@
import { ApplicationConfiguration, AuthService, ConfigStateService } from '@abp/ng.core';
import { Component, OnInit } from '@angular/core';
import {
ApplicationConfiguration,
AuthService,
ConfigStateService,
EnvironmentService,
} from '@abp/ng.core';
import { Component } from '@angular/core';
import { Router } from '@angular/router';
import { Observable } from 'rxjs';
@ -8,7 +13,7 @@ import { Observable } from 'rxjs';
// tslint:disable-next-line: component-max-inline-declarations
template: `
<ng-template #loginBtn>
<a role="button" class="nav-link" routerLink="/account/login">{{
<a role="button" class="nav-link pointer" (click)="initLogin()">{{
'AbpAccount::Login' | abpLocalization
}}</a>
</ng-template>
@ -36,7 +41,7 @@ import { Observable } from 'rxjs';
aria-labelledby="dropdownMenuLink"
[class.d-block]="smallScreen && currentUserDropdown.isOpen()"
>
<a class="dropdown-item" routerLink="/account/manage-profile"
<a class="dropdown-item" [href]="manageProfileUrl"
><i class="fa fa-cog mr-1"></i>{{ 'AbpAccount::ManageYourProfile' | abpLocalization }}</a
>
<a class="dropdown-item" href="javascript:void(0)" (click)="logout()"
@ -46,7 +51,7 @@ import { Observable } from 'rxjs';
</div>
`,
})
export class CurrentUserComponent implements OnInit {
export class CurrentUserComponent {
currentUser$: Observable<ApplicationConfiguration.CurrentUser> = this.configState.getOne$(
'currentUser',
);
@ -55,13 +60,22 @@ export class CurrentUserComponent implements OnInit {
return window.innerWidth < 992;
}
get manageProfileUrl() {
return `${this.environment.getEnvironment().oAuthConfig.issuer}/Account/Manage?returnUrl=${
window.location.href
}`;
}
constructor(
private authService: AuthService,
private router: Router,
private configState: ConfigStateService,
private environment: EnvironmentService,
) {}
ngOnInit() {}
initLogin() {
this.authService.initLogin();
}
logout() {
this.authService.logout().subscribe(() => {

@ -21,8 +21,6 @@
"@abp/ng.theme.shared/extensions": ["packages/theme-shared/extensions/src/public-api.ts"],
"@abp/ng.components/tree": ["packages/components/tree/src/public-api.ts"],
"@abp/ng.theme.basic": ["packages/theme-basic/src/public-api.ts"],
"@abp/ng.account": ["packages/account/src/public-api.ts"],
"@abp/ng.account/config": ["packages/account/config/src/public-api.ts"],
"@abp/ng.identity": ["packages/identity/src/public-api.ts"],
"@abp/ng.identity/config": ["packages/identity/config/src/public-api.ts"],
"@abp/ng.tenant-management": ["packages/tenant-management/src/public-api.ts"],

@ -12,7 +12,6 @@
},
"private": true,
"dependencies": {
"@abp/ng.account": "~3.3.1",
"@abp/ng.identity": "~3.3.1",
"@abp/ng.setting-management": "~3.3.1",
"@abp/ng.tenant-management": "~3.3.1",

@ -5,26 +5,21 @@ const routes: Routes = [
{
path: '',
pathMatch: 'full',
loadChildren: () => import('./home/home.module').then((m) => m.HomeModule),
},
{
path: 'account',
loadChildren: () =>
import('@abp/ng.account').then((m) => m.AccountModule.forLazy({ redirectUrl: '/' })),
loadChildren: () => import('./home/home.module').then(m => m.HomeModule),
},
{
path: 'identity',
loadChildren: () => import('@abp/ng.identity').then((m) => m.IdentityModule.forLazy()),
loadChildren: () => import('@abp/ng.identity').then(m => m.IdentityModule.forLazy()),
},
{
path: 'tenant-management',
loadChildren: () =>
import('@abp/ng.tenant-management').then((m) => m.TenantManagementModule.forLazy()),
import('@abp/ng.tenant-management').then(m => m.TenantManagementModule.forLazy()),
},
{
path: 'setting-management',
loadChildren: () =>
import('@abp/ng.setting-management').then((m) => m.SettingManagementModule.forLazy()),
import('@abp/ng.setting-management').then(m => m.SettingManagementModule.forLazy()),
},
];

@ -1,4 +1,3 @@
import { AccountConfigModule } from '@abp/ng.account/config';
import { CoreModule } from '@abp/ng.core';
import { registerLocale } from '@abp/ng.core/locale';
import { IdentityConfigModule } from '@abp/ng.identity/config';
@ -25,7 +24,6 @@ import { APP_ROUTE_PROVIDER } from './route.provider';
registerLocaleFn: registerLocale(),
}),
ThemeSharedModule.forRoot(),
AccountConfigModule.forRoot(),
IdentityConfigModule.forRoot(),
TenantManagementConfigModule.forRoot(),
SettingManagementConfigModule.forRoot(),

@ -1,4 +1,4 @@
import { Config } from '@abp/ng.core';
import { Environment } from '@abp/ng.core';
const baseUrl = 'http://localhost:4200';
@ -22,4 +22,4 @@ export const environment = {
rootNamespace: 'MyCompanyName.MyProjectName',
},
},
} as Config.Environment;
} as Environment;

@ -1,4 +1,4 @@
import { Config } from '@abp/ng.core';
import { Environment } from '@abp/ng.core';
const baseUrl = 'http://localhost:4200';
@ -22,4 +22,4 @@ export const environment = {
rootNamespace: 'MyCompanyName.MyProjectName',
},
},
} as Config.Environment;
} as Environment;

File diff suppressed because it is too large Load Diff

@ -11,6 +11,7 @@ using Volo.Abp.IdentityServer.ApiResources;
using Volo.Abp.IdentityServer.ApiScopes;
using Volo.Abp.IdentityServer.Clients;
using Volo.Abp.IdentityServer.IdentityResources;
using Volo.Abp.MultiTenancy;
using Volo.Abp.PermissionManagement;
using Volo.Abp.Uow;
using ApiResource = Volo.Abp.IdentityServer.ApiResources.ApiResource;
@ -28,6 +29,7 @@ namespace MyCompanyName.MyProjectName.IdentityServer
private readonly IGuidGenerator _guidGenerator;
private readonly IPermissionDataSeeder _permissionDataSeeder;
private readonly IConfiguration _configuration;
private readonly ICurrentTenant _currentTenant;
public IdentityServerDataSeedContributor(
IClientRepository clientRepository,
@ -36,7 +38,8 @@ namespace MyCompanyName.MyProjectName.IdentityServer
IIdentityResourceDataSeeder identityResourceDataSeeder,
IGuidGenerator guidGenerator,
IPermissionDataSeeder permissionDataSeeder,
IConfiguration configuration)
IConfiguration configuration,
ICurrentTenant currentTenant)
{
_clientRepository = clientRepository;
_apiResourceRepository = apiResourceRepository;
@ -45,15 +48,19 @@ namespace MyCompanyName.MyProjectName.IdentityServer
_guidGenerator = guidGenerator;
_permissionDataSeeder = permissionDataSeeder;
_configuration = configuration;
_currentTenant = currentTenant;
}
[UnitOfWork]
public virtual async Task SeedAsync(DataSeedContext context)
{
await _identityResourceDataSeeder.CreateStandardResourcesAsync();
await CreateApiResourcesAsync();
await CreateApiScopesAsync();
await CreateClientsAsync();
using (_currentTenant.Change(context?.TenantId))
{
await _identityResourceDataSeeder.CreateStandardResourcesAsync();
await CreateApiResourcesAsync();
await CreateApiScopesAsync();
await CreateClientsAsync();
}
}
private async Task CreateApiScopesAsync()

@ -15,7 +15,6 @@
},
"private": true,
"dependencies": {
"@abp/ng.account": "~3.3.1",
"@abp/ng.identity": "~3.3.1",
"@abp/ng.setting-management": "~3.3.1",
"@abp/ng.tenant-management": "~3.3.1",
@ -55,4 +54,4 @@
"tslint": "~6.1.0",
"typescript": "~4.0.3"
}
}
}

@ -5,40 +5,26 @@ const routes: Routes = [
{
path: '',
pathMatch: 'full',
loadChildren: () => import('./home/home.module').then((m) => m.HomeModule),
},
{
path: 'account',
loadChildren: () =>
import('@abp/ng.account').then((m) =>
m.AccountModule.forLazy({ redirectUrl: '/' })
),
loadChildren: () => import('./home/home.module').then(m => m.HomeModule),
},
{
path: 'identity',
loadChildren: () =>
import('@abp/ng.identity').then((m) => m.IdentityModule.forLazy()),
loadChildren: () => import('@abp/ng.identity').then(m => m.IdentityModule.forLazy()),
},
{
path: 'tenant-management',
loadChildren: () =>
import('@abp/ng.tenant-management').then((m) =>
m.TenantManagementModule.forLazy()
),
import('@abp/ng.tenant-management').then(m => m.TenantManagementModule.forLazy()),
},
{
path: 'setting-management',
loadChildren: () =>
import('@abp/ng.setting-management').then((m) =>
m.SettingManagementModule.forLazy()
),
import('@abp/ng.setting-management').then(m => m.SettingManagementModule.forLazy()),
},
{
path: 'my-project-name',
loadChildren: () =>
import('@my-company-name/my-project-name').then((m) =>
m.MyProjectNameModule.forLazy()
),
import('@my-company-name/my-project-name').then(m => m.MyProjectNameModule.forLazy()),
},
];

@ -1,4 +1,3 @@
import { AccountConfigModule } from '@abp/ng.account/config';
import { CoreModule } from '@abp/ng.core';
import { registerLocale } from '@abp/ng.core/locale';
import { IdentityConfigModule } from '@abp/ng.identity/config';
@ -28,7 +27,6 @@ import { ThemeBasicModule } from '@abp/ng.theme.basic';
skipGetAppConfiguration: false,
}),
ThemeSharedModule.forRoot(),
AccountConfigModule.forRoot(),
IdentityConfigModule.forRoot(),
MyProjectNameConfigModule.forRoot(),
TenantManagementConfigModule.forRoot(),

@ -1,4 +1,6 @@
import { Config } from '@abp/ng.core';
import { Environment } from '@abp/ng.core';
const baseUrl = 'http://localhost:4200';
export const environment = {
production: true,
@ -9,11 +11,10 @@ export const environment = {
},
oAuthConfig: {
issuer: 'https://localhost:44301',
redirectUri: baseUrl,
clientId: 'MyProjectName_ConsoleTestApp',
dummyClientSecret: '1q2w3e*',
scope: 'MyProjectName',
oidc: false,
requireHttps: true,
responseType: 'code',
scope: 'offline_access MyProjectName',
},
apis: {
default: {
@ -25,4 +26,4 @@ export const environment = {
rootNamespace: 'MyCompanyName.MyProjectName',
},
},
} as Config.Environment;
} as Environment;

@ -1,4 +1,6 @@
import { Config } from '@abp/ng.core';
import { Environment } from '@abp/ng.core';
const baseUrl = 'http://localhost:4200';
export const environment = {
production: false,
@ -9,11 +11,10 @@ export const environment = {
},
oAuthConfig: {
issuer: 'https://localhost:44301',
redirectUri: baseUrl,
clientId: 'MyProjectName_ConsoleTestApp',
dummyClientSecret: '1q2w3e*',
scope: 'MyProjectName',
oidc: false,
requireHttps: true,
responseType: 'code',
scope: 'offline_access MyProjectName',
},
apis: {
default: {
@ -25,4 +26,4 @@ export const environment = {
rootNamespace: 'MyCompanyName.MyProjectName',
},
},
} as Config.Environment;
} as Environment;

@ -11,6 +11,7 @@ using Volo.Abp.IdentityServer.ApiResources;
using Volo.Abp.IdentityServer.ApiScopes;
using Volo.Abp.IdentityServer.Clients;
using Volo.Abp.IdentityServer.IdentityResources;
using Volo.Abp.MultiTenancy;
using Volo.Abp.PermissionManagement;
using Volo.Abp.Uow;
using ApiResource = Volo.Abp.IdentityServer.ApiResources.ApiResource;
@ -28,6 +29,7 @@ namespace MyCompanyName.MyProjectName.IdentityServer
private readonly IGuidGenerator _guidGenerator;
private readonly IPermissionDataSeeder _permissionDataSeeder;
private readonly IConfiguration _configuration;
private readonly ICurrentTenant _currentTenant;
public IdentityServerDataSeedContributor(
IClientRepository clientRepository,
@ -36,7 +38,8 @@ namespace MyCompanyName.MyProjectName.IdentityServer
IIdentityResourceDataSeeder identityResourceDataSeeder,
IGuidGenerator guidGenerator,
IPermissionDataSeeder permissionDataSeeder,
IConfiguration configuration)
IConfiguration configuration,
ICurrentTenant currentTenant)
{
_clientRepository = clientRepository;
_apiResourceRepository = apiResourceRepository;
@ -45,15 +48,19 @@ namespace MyCompanyName.MyProjectName.IdentityServer
_guidGenerator = guidGenerator;
_permissionDataSeeder = permissionDataSeeder;
_configuration = configuration;
_currentTenant = currentTenant;
}
[UnitOfWork]
public virtual async Task SeedAsync(DataSeedContext context)
{
await _identityResourceDataSeeder.CreateStandardResourcesAsync();
await CreateApiResourcesAsync();
await CreateApiScopesAsync();
await CreateClientsAsync();
using (_currentTenant.Change(context?.TenantId))
{
await _identityResourceDataSeeder.CreateStandardResourcesAsync();
await CreateApiResourcesAsync();
await CreateApiScopesAsync();
await CreateClientsAsync();
}
}
private async Task CreateApiScopesAsync()

@ -2,26 +2,32 @@
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Guids;
using Volo.Abp.MultiTenancy;
namespace MyCompanyName.MyProjectName
{
public class MyProjectNameDataSeedContributor : IDataSeedContributor, ITransientDependency
{
private readonly IGuidGenerator _guidGenerator;
private readonly ICurrentTenant _currentTenant;
public MyProjectNameDataSeedContributor(
IGuidGenerator guidGenerator)
IGuidGenerator guidGenerator, ICurrentTenant currentTenant)
{
_guidGenerator = guidGenerator;
_currentTenant = currentTenant;
}
public Task SeedAsync(DataSeedContext context)
{
/* Instead of returning the Task.CompletedTask, you can insert your test data
* at this point!
*/
return Task.CompletedTask;
using (_currentTenant.Change(context?.TenantId))
{
return Task.CompletedTask;
}
}
}
}
}

Loading…
Cancel
Save