Browse Source

Merge branch 'rel-8.1' into audit-log-localization

pull/19258/head
ahmetfarukulu 2 years ago
parent
commit
f12e3f9b61
  1. 2
      docs/en/Background-Workers.md
  2. 3
      docs/en/UI/Angular/Data-Table-Column-Extensions.md
  3. 8
      docs/en/UI/Angular/Dynamic-Form-Extensions.md
  4. 8
      docs/en/UI/Angular/Entity-Action-Extensions.md
  5. 4
      docs/zh-Hans/Background-Workers.md
  6. 14
      framework/src/Volo.Abp.BlazoriseUI/AbpCrudPageBase.cs
  7. 20
      framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepository.cs
  8. 4
      modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/CreateModal.cshtml
  9. 3
      modules/permission-management/src/Volo.Abp.PermissionManagement.Blazor/Components/PermissionManagementModal.razor
  10. 6
      modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Pages/AbpPermissionManagement/PermissionManagementModal.cshtml
  11. 9
      modules/tenant-management/test/Volo.Abp.TenantManagement.Domain.Tests/Volo/Abp/TenantManagement/TenantStore_Tests.cs
  12. 2
      npm/ng-packs/packages/components/extensible/src/lib/components/extensible-table/extensible-table.component.html
  13. 34
      npm/ng-packs/packages/components/extensible/src/lib/components/extensible-table/extensible-table.component.ts
  14. 37
      npm/ng-packs/packages/components/extensible/src/lib/components/grid-actions/grid-actions.component.html
  15. 3
      npm/ng-packs/packages/components/extensible/src/lib/components/grid-actions/grid-actions.component.ts
  16. 3
      npm/ng-packs/packages/components/extensible/src/lib/models/entity-actions.ts
  17. 2
      npm/ng-packs/packages/core/src/lib/services/list.service.ts
  18. 9
      npm/ng-packs/packages/theme-shared/src/lib/directives/ngx-datatable-list.directive.ts

2
docs/en/Background-Workers.md

@ -72,7 +72,7 @@ public class PassiveUserCheckerWorker : AsyncPeriodicBackgroundWorkerBase
}
````
* `AsyncPeriodicBackgroundWorkerBase` uses the `AbpTimer` (a thread-safe timer) object to determine **the period**. We can set its `Period` property in the constructor.
* `AsyncPeriodicBackgroundWorkerBase` uses the `AbpAsyncTimer` (a thread-safe timer) object to determine **the period**. We can set its `Period` property in the constructor.
* It required to implement the `DoWorkAsync` method to **execute** the periodic work.
* It is a good practice to **resolve dependencies** from the `PeriodicBackgroundWorkerContext` instead of constructor injection. Because `AsyncPeriodicBackgroundWorkerBase` uses a `IServiceScope` that is **disposed** when your work finishes.
* `AsyncPeriodicBackgroundWorkerBase` **catches and logs exceptions** thrown by the `DoWorkAsync` method.

3
docs/en/UI/Angular/Data-Table-Column-Extensions.md

@ -220,6 +220,7 @@ type EntityPropOptions<R = any> = {
permission?: string;
visible?: PropPredicate<R>;
columnVisible?: ColumnPredicate;
tooltip?: FormPropTooltip;
};
```
@ -234,6 +235,7 @@ As you see, passing `type` and `name` is enough to create an entity prop. Here i
- **permission** is the permission context which will be used to decide if a column for this entity prop should be displayed to the user or not. (_default:_ `undefined`)
- **visible** is a predicate that will be used to decide if the cell content of this entity prop should be displayed on the table or not based on the data record. (_default:_ `() => true`)
- **columnVisible** is a predicate that will be used to decide if the column of this entity prop should be displayed on the table or not. (_default:_ `() => true`)
- **tooltip** . is a tooltip for table column (_default:_ `undefined`)
> Important Note: Do not use record in visibility predicates. First of all, the table header checks it too and the record will be `undefined`. Second, if some cells are displayed and others are not, the table will be broken. Use the `valueResolver` and render an empty cell when you need to hide a specific cell.
>
@ -272,6 +274,7 @@ const options: EntityPropOptions<IdentityUserDto> = {
const sessionStateService = getInjected(SessionStateService);
return !sessionStateService.getTenant()?.isAvailable; // hide this column when the tenant is available.
},
tooltip: { text: 'AbpIdentity::EmailAddress_Tooltip', placement: 'top' }
};
const prop = new EntityProp(options);

8
docs/en/UI/Angular/Dynamic-Form-Extensions.md

@ -163,6 +163,8 @@ type FormPropOptions<R = any> = {
options?: PropCallback<R, Observable<ABP.Option<any>[]>>;
autocomplete?: string;
isExtra? boolean;
formText?: string;
tooltip?: FormPropTooltip;
};
```
@ -182,6 +184,8 @@ As you see, passing `type` and `name` is enough to create a form prop. Here is w
- **options** is a callback that is called when a dropdown is needed. It must return an observable. (_default:_ `undefined`)
- **autocomplete** will be set as the `autocomplete` attribute of the input for the field. Please check [possible values](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/autocomplete#Values). (_default:_ `'off'`)
- **isExtra** indicates this prop is an object extension. When `true`, the value of the field will be mapped from and to `extraProperties` of the entity. (_default:_ `undefined`)
- **formText** is the definition of the field. Placed under the field. (_default:_ `undefined`)
- **tooltip** is the tooltip for the field placed near of the label (_default:_ `undefined`)
> Important Note: Do not use `record` property of `PropData` in create form predicates and callbacks, because it will be `undefined`. You can use it on edit form contributors though.
@ -235,7 +239,9 @@ const options: FormPropOptions<IdentityUserDto> = {
},
autocomplete: 'off',
isExtra: true,
template: undefined | Type<any> // Custom angular component
template: undefined | Type<any>, // Custom angular component
tooltip: { text: 'Default::MyPropName_Tooltip', placement: 'top' },
formText: 'Default::MyPropName_Description',
};
const prop = new FormProp(options);

8
docs/en/UI/Angular/Entity-Action-Extensions.md

@ -311,6 +311,8 @@ type EntityActionOptions<R = any> = {
visible?: ActionPredicate<R>,
btnClass?: string,
btnStyle?: string,
showOnlyIcon?: boolean,
tooltip?: FormPropTooltip;
};
```
@ -323,7 +325,9 @@ As you see, passing `action` and `text` is enough to create an entity action. He
- **visible** is a predicate that will be used to decide if the current record should have this grid action or not. (_default:_ `() => true`)
- **btnClass** is the classes that will be applied to the button. (_default:_ `'btn btn-primary text-center'`)
- **btnStyle** is the styles that will be applied to the button. (_default:_ `''`)
- **showOnlyIcon** is shows only the icon itself. (_default:_ `false`)
- **tooltip** is only available in single entity action button. Adds an tooltip for button. (_default:_ undefined)
You may find a full example below.
### EntityAction\<R = any\>
@ -342,6 +346,8 @@ const options: EntityActionOptions<IdentityUserDto> = {
visible: data => data.record.isLockedOut,
btnClass:'btn btn-warning text-center',
btnStyle: '', //Adds inline style
showOnlyIcon: true,
tooltip: { text: 'AbpIdentity::Edit', placement: 'top' }
};
const action = new EntityAction(options);

4
docs/zh-Hans/Background-Workers.md

@ -44,7 +44,7 @@ public class MyWorker : BackgroundWorkerBase
public class PassiveUserCheckerWorker : AsyncPeriodicBackgroundWorkerBase
{
public PassiveUserCheckerWorker(
AbpTimer timer,
AbpAsyncTimer timer,
IServiceScopeFactory serviceScopeFactory
) : base(
timer,
@ -71,7 +71,7 @@ public class PassiveUserCheckerWorker : AsyncPeriodicBackgroundWorkerBase
}
````
* `AsyncPeriodicBackgroundWorkerBase` 使用 `AbpTimer`(线程安全定时器)对象来确定**时间段**. 我们可以在构造函数中设置了`Period` 属性.
* `AsyncPeriodicBackgroundWorkerBase` 使用 `AbpAsyncTimer`(线程安全定时器)对象来确定**时间段**. 我们可以在构造函数中设置了`Period` 属性.
* 它需要实现 `DoWorkAsync` 方法**执行**定期任务.
* 最好使用 `PeriodicBackgroundWorkerContext` **解析依赖** 而不是构造函数. 因为 `AsyncPeriodicBackgroundWorkerBase` 使用 `IServiceScope` 在你的任务执行结束时会对其 **disposed**.
* `AsyncPeriodicBackgroundWorkerBase` **捕获并记录**`DoWorkAsync` 方法抛出的 **异常**.

14
framework/src/Volo.Abp.BlazoriseUI/AbpCrudPageBase.cs

@ -306,10 +306,12 @@ public abstract class AbpCrudPageBase<
protected virtual async Task SearchEntitiesAsync()
{
var currentPage = CurrentPage;
CurrentPage = 1;
await GetEntitiesAsync();
if (currentPage == 1)
{
await GetEntitiesAsync();
}
await InvokeAsync(StateHasChanged);
}
@ -596,12 +598,12 @@ public abstract class AbpCrudPageBase<
await SetEntityActionsAsync();
}
protected virtual ValueTask SetEntityActionsAsync()
{
return ValueTask.CompletedTask;
}
private async ValueTask TrySetTableColumnsAsync()
{
if (IsDisposed)
@ -614,7 +616,7 @@ public abstract class AbpCrudPageBase<
protected virtual ValueTask SetTableColumnsAsync()
{
return ValueTask.CompletedTask;
}

20
framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepository.cs

@ -507,11 +507,21 @@ public class MongoDbRepository<TMongoDbContext, TEntity>
var dbContext = await GetDbContextAsync(cancellationToken);
var collection = dbContext.Collection<TEntity>();
await collection.DeleteManyAsync(
dbContext.SessionHandle,
Builders<TEntity>.Filter.Where(predicate),
cancellationToken: cancellationToken
);
if (dbContext.SessionHandle != null)
{
await collection.DeleteManyAsync(
dbContext.SessionHandle,
Builders<TEntity>.Filter.Where(predicate),
cancellationToken: cancellationToken
);
}
else
{
await collection.DeleteManyAsync(
Builders<TEntity>.Filter.Where(predicate),
cancellationToken: cancellationToken
);
}
}
[Obsolete("Use GetQueryableAsync method.")]

4
modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/CreateModal.cshtml

@ -21,7 +21,7 @@
<abp-modal-body>
<abp-tabs name="create-user-modal-tabs">
<abp-tab title="@L["UserInformations"].Value">
<div class="mt-3" >
<div >
@* TODO: Can we use dynamic form? *@
<abp-input asp-for="UserInfo.UserName" />
<abp-input asp-for="UserInfo.Name" />
@ -72,7 +72,7 @@
</div>
</abp-tab>
<abp-tab title="@L["Roles"].Value">
<div class="mt-3" >
<div>
@for (var i = 0; i < Model.Roles.Length; i++)
{
var role = Model.Roles[i];

3
modules/permission-management/src/Volo.Abp.PermissionManagement.Blazor/Components/PermissionManagementModal.razor

@ -43,9 +43,6 @@
@foreach (var group in _groups)
{
<TabPanel Name="@GetNormalizedGroupName(group.Name)">
<h4>@group.DisplayName</h4>
<Divider />
<Field>
<Check

6
modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Pages/AbpPermissionManagement/PermissionManagementModal.cshtml

@ -16,7 +16,7 @@
<abp-modal-header title="@(L["Permissions"].Value) - @(HttpUtility.HtmlEncode(Model.EntityDisplayName))"></abp-modal-header>
<abp-modal-body class="custom-scroll-container">
<abp-input asp-for="SelectAllInAllTabs" check-box-hidden-input-render-mode="CheckBoxHiddenInputRenderMode.None" label="@L["SelectAllInAllTabs"].Value" disabled="@Model.Groups.All(group => group.IsDisabled(Model.ProviderName))"/>
<hr class="mt-2 mb-2"/>
<hr class="my-2"/>
<input asp-for="@Model.ProviderKey"/>
<input asp-for="@Model.ProviderName"/>
<abp-tabs name="PermissionsTabs" tab-style="PillVertical" vertical-header-size="_4" class="custom-scroll-container">
@ -24,10 +24,8 @@
{
var group = Model.Groups[i];
<abp-tab title="@group.DisplayName" name="v-pills-tab-@group.GetNormalizedGroupName()">
<h4>@group.DisplayName</h4>
<hr class="mt-2 mb-3"/>
<div class="w-100" style="max-height: 640px;overflow-y: auto">
<div class="ps-1 pt-1">
<div class="ps-1">
<abp-input asp-for="@group.IsAllPermissionsGranted"
check-box-hidden-input-render-mode="CheckBoxHiddenInputRenderMode.None"
name="SelectAllInThisTab"

9
modules/tenant-management/test/Volo.Abp.TenantManagement.Domain.Tests/Volo/Abp/TenantManagement/TenantStore_Tests.cs

@ -35,4 +35,13 @@ public class TenantStore_Tests : AbpTenantManagementDomainTestBase
(await _tenantStore.FindAsync(acme.Id)).ShouldNotBeNull();
}
[Fact]
public async Task GetListAsync()
{
var tenants = await _tenantRepository.GetListAsync();
tenants.ShouldNotBeNull();
tenants.Count.ShouldBe(3);
}
}

2
npm/ng-packs/packages/components/extensible/src/lib/components/extensible-table/extensible-table.component.html

@ -3,6 +3,8 @@
[rows]="data"
[count]="recordsTotal"
[list]="list"
[offset]="list?.page"
(page)="setPage($event)"
(activate)="tableActivate.emit($event)"
>
@if (actionsTemplate || (actionList.length && hasAtLeastOnePermittedAction)) {

34
npm/ng-packs/packages/components/extensible/src/lib/components/extensible-table/extensible-table.component.ts

@ -1,7 +1,6 @@
import {
ABP,
ConfigStateService,
CoreModule,
ConfigStateService,
getShortDateFormat,
getShortDateShortTimeFormat,
getShortTimeFormat,
@ -13,9 +12,7 @@ import {
import {
AsyncPipe,
formatDate,
NgComponentOutlet,
NgFor,
NgIf,
NgComponentOutlet,
NgTemplateOutlet,
} from '@angular/common';
import {
@ -175,7 +172,32 @@ export class ExtensibleTableComponent<R = any> implements OnChanges {
);
}
ngOnChanges({ data }: SimpleChanges) {
setPage({ offset }) {
this.list.page = offset;
}
ngOnChanges({ data, recordsTotal }: SimpleChanges) {
if (data?.currentValue.length < 1 && recordsTotal?.currentValue > 0) {
let maxPage = Math.floor(Number(recordsTotal?.currentValue / this.list.maxResultCount));
if(recordsTotal?.currentValue < this.list.maxResultCount) {
this.list.page = 0;
return;
}
if (recordsTotal?.currentValue % this.list.maxResultCount === 0) {
maxPage -= 1;
}
if (this.list.page < maxPage) {
this.list.page = this.list.page;
return;
}
this.list.page = maxPage;
return;
}
if (!data?.currentValue) return;
if (data.currentValue.length < 1) {

37
npm/ng-packs/packages/components/extensible/src/lib/components/grid-actions/grid-actions.component.html

@ -53,16 +53,31 @@
<ng-template #btnTmp let-action>
@if (action.visible(data)) {
<button
*abpPermission="action.permission; runChangeDetection: false"
(click)="action.action(data)"
type="button"
[class]="action.btnClass"
[style]="action.btnStyle"
>
<ng-container
*ngTemplateOutlet="buttonContentTmp; context: { $implicit: action }"
></ng-container>
</button>
@if (action.tooltip) {
<button
*abpPermission="action.permission; runChangeDetection: false"
(click)="action.action(data)"
type="button"
[class]="action.btnClass"
[style]="action.btnStyle"
[ngbTooltip]="action.tooltip.text | abpLocalization" [placement]="action.tooltip.placement || 'auto'" container="body"
>
<ng-container
*ngTemplateOutlet="buttonContentTmp; context: { $implicit: action }"
></ng-container>
</button>
} @else {
<button
*abpPermission="action.permission; runChangeDetection: false"
(click)="action.action(data)"
type="button"
[class]="action.btnClass"
[style]="action.btnStyle"
>
<ng-container
*ngTemplateOutlet="buttonContentTmp; context: { $implicit: action }"
></ng-container>
</button>
}
}
</ng-template>

3
npm/ng-packs/packages/components/extensible/src/lib/components/grid-actions/grid-actions.component.ts

@ -8,7 +8,7 @@ import {
import { EntityAction, EntityActionList } from '../../models/entity-actions';
import { EXTENSIONS_ACTION_TYPE } from '../../tokens/extensions.token';
import { AbstractActionsComponent } from '../abstract-actions/abstract-actions.component';
import { NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap';
import { NgbDropdownModule, NgbTooltipModule } from '@ng-bootstrap/ng-bootstrap';
import { LocalizationModule, PermissionDirective } from '@abp/ng.core';
import { EllipsisDirective } from '@abp/ng.theme.shared';
import { NgClass, NgTemplateOutlet } from '@angular/common';
@ -23,6 +23,7 @@ import { NgClass, NgTemplateOutlet } from '@angular/common';
NgClass,
LocalizationModule,
NgTemplateOutlet,
NgbTooltipModule
],
selector: 'abp-grid-actions',
templateUrl: './grid-actions.component.html',

3
npm/ng-packs/packages/components/extensible/src/lib/models/entity-actions.ts

@ -8,6 +8,7 @@ import {
Actions,
ActionsFactory,
} from './actions';
import { FormPropTooltip } from './form-props';
export class EntityActionList<R = any> extends ActionList<R, EntityAction<R>> {}
@ -25,6 +26,7 @@ export class EntityAction<R = any> extends Action<R> {
readonly btnClass?: string;
readonly btnStyle?: string;
readonly showOnlyIcon?: boolean;
readonly tooltip?: FormPropTooltip;
constructor(options: EntityActionOptions<R>) {
super(options.permission || '', options.visible, options.action);
@ -33,6 +35,7 @@ export class EntityAction<R = any> extends Action<R> {
this.btnClass = options.btnClass || 'btn btn-primary text-center';
this.btnStyle = options.btnStyle;
this.showOnlyIcon = options.showOnlyIcon || false;
this.tooltip = options.tooltip;
}
static create<R = any>(options: EntityActionOptions<R>) {

2
npm/ng-packs/packages/core/src/lib/services/list.service.ts

@ -150,7 +150,7 @@ export class ListService<QueryParamsType = ABP.PageQueryParams | any> implements
this._query$.next({
filter: this._filter || undefined,
maxResultCount: this._maxResultCount,
skipCount: this._filter ? 0 : this._page * this._maxResultCount,
skipCount: this._page * this._maxResultCount,
sorting: this._sortOrder ? `${this._sortKey} ${this._sortOrder}` : undefined,
} as any as QueryParamsType);
}

9
npm/ng-packs/packages/theme-shared/src/lib/directives/ngx-datatable-list.directive.ts

@ -53,14 +53,6 @@ export class NgxDatatableListDirective implements OnChanges, OnDestroy, OnInit {
};
}
private subscribeToPage() {
const sub = this.table.page.subscribe(({ offset }) => {
this.list.page = offset;
this.table.offset = offset;
});
this.subscription.add(sub);
}
private subscribeToSort() {
const sub = this.table.sort.subscribe(({ sorts: [{ prop, dir }] }) => {
if (prop === this.list.sortKey && this.list.sortOrder === 'desc') {
@ -101,7 +93,6 @@ export class NgxDatatableListDirective implements OnChanges, OnDestroy, OnInit {
}
ngOnInit() {
this.subscribeToPage();
this.subscribeToSort();
}
}

Loading…
Cancel
Save